Skip to content

Conversation

rudransh-shrivastava
Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava commented Sep 3, 2025

Resolves #2120

Proposed change

Migrate Apollo Client to v4

  • Upgrade apollo/client to 4.0.0
  • Run Apollo codemod
  • Replace old queries with new generataed typed-document-node queries
  • Change types to have more optional fields.
  • Change date/time/datetimes (createdAt/updatedAt) fields to be number | string
  • Update enums to be UPPERCASE as per convention
  • Update unit tests to import correct client and use generated queries
  • Update e2e tests to use correct dates/enums.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

Copy link
Contributor

coderabbitai bot commented Sep 3, 2025

Summary by CodeRabbit

  • New Features
    • Organization pages now display recent pull requests and releases.
  • Improvements
    • More consistent status and experience-level formatting across mentorship UI.
    • Enhanced loading and error states on committee and related pages (clear spinners and 404s when data is missing).
    • Broader leader details support (company and location) where shown.
  • Chores
    • Upgraded Apollo Client to v4 and adopted generated GraphQL documents across the app.
    • Added rxjs dependency.
    • Updated GraphQL code generation and type usage for better stability and consistency.

Walkthrough

Migrate frontend to Apollo Client v4 and GraphQL codegen artifacts: update dependencies, switch hook imports to @apollo/client/react, replace many GET_* constants with generated Get*Document DocumentNodes, replace createHttpLink with HttpLink, tighten route param typings, introduce generated enums/types, and relax many TypeScript model fields.

Changes

Cohort / File(s) Summary
Dependencies
frontend/package.json
Bump @apollo/client^4.0.0; add rxjs ^7.8.2.
Apollo wiring & link helpers
frontend/src/server/apolloClient.ts, frontend/src/utils/helpers/apolloClient.ts, frontend/src/wrappers/provider.tsx
Replace createHttpLink(...) with new HttpLink(...); update imports; change provider/hook import to @apollo/client/react.
Global hook imports & tests
frontend/src/**/*, frontend/__tests__/**/*
Change Apollo hook imports from @apollo/client@apollo/client/react; update many Jest mocks to target @apollo/client/react and adjust mock casting/typing.
Query constants → generated DocumentNodes (bulk)
frontend/src/app/about/page.tsx, frontend/src/app/page.tsx, frontend/src/app/chapters/..., frontend/src/app/committees/..., frontend/src/app/members/..., frontend/src/app/projects/..., frontend/src/app/organizations/..., frontend/src/app/snapshots/..., frontend/src/app/mentorship/**, frontend/src/app/my/mentorship/**, frontend/src/app/settings/api-keys/page.tsx, etc.
Replace local GET_* constants with codegen Get*Document imports; update useQuery/useMutation calls and related imports; preserve variables and control flow; add/adjust useParams generics in many routes.
Mentorship pages & enums
frontend/src/app/mentorship/**, frontend/src/app/my/mentorship/**, tests
Import generated enums (ProgramStatusEnum, ExperienceLevelEnum) from types/__generated__/graphql; update enum usages and test fixtures to match generated enum members and casing.
Projects dashboard & Ordering
frontend/src/app/projects/dashboard/metrics/page.tsx, frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx, frontend/src/app/projects/dashboard/page.tsx
Swap to generated documents and Ordering enum; replace string literals ('ASC'/'DESC') with Ordering enum values; update state types, handler signatures, fetchMore ordering, and component prop types.
Type relaxations & generated types
frontend/src/types/* (chapter, committee, healthMetrics, issue, mentorship, milestone, organization, project, pullRequest, release, user, etc.)
Make many previously-required fields optional; remove local mentorship enums in favor of generated enums; remove UserDetails alias and generic on User; broaden date types.
GraphQL codegen config
frontend/graphql-codegen.ts
Change defaultScalarType to any; add per-file scalar mapping for Date/DateTime → `string
Server query body tweak
frontend/src/server/queries/userQueries.ts
Add company and location fields to GET_LEADER_DATA selection.
Structured data helper
frontend/src/utils/structuredData.ts
Add formatISODate(input); update generateProfilePageStructuredData to accept User and use helper for date normalization.
Component & small UI tweaks
frontend/src/components/ModuleForm.tsx, frontend/src/components/SingleModuleCard.tsx, frontend/src/components/ProgramActions.tsx
Use generated SearchProjectNamesDocument, replace experience-level literals with ExperienceLevelEnum values, normalize casing with toLowerCase() before upperFirst, and change setStatus prop to accept ProgramStatusEnum values.
Tests: document/enums/typing updates
frontend/__tests__/unit/** (e.g., About, ApiKeysPage, ProgramCard, many pages/components)
Update mocks and assertions to reference generated documents/enums; adjust mock implementations and casts; adapt expectations where types changed (e.g., expiresAt string ISO).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • kasya
  • arkid15r

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR also introduces broad, non-migration changes that affect public types and APIs, including widespread relaxation of type fields (Chapter, Project, User, HealthMetrics, Organization, etc.), removal of the User generic and UserDetails alias, changing graphql-codegen defaultScalarType to "any" with per-file scalar mappings, adding rxjs, new helpers and signature changes (formatISODate and generateProfilePageStructuredData), and public prop/signature changes (e.g., ProgramActions.setStatus and ProjectsDashboard ordering types); these material API/type-shape changes are outside the narrow Apollo upgrade objective and increase review scope and risk. Please split or explicitly justify the large type and API-shape changes into separate PRs (or document migration rationale and compatibility tests) so the Apollo client migration remains focused and reviewable, and ensure CI coverage for the API-impacting changes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Migrate Apollo Client to v4" is a concise, single-sentence summary that directly describes the primary change (upgrading to Apollo Client v4 and associated codemod/queries changes) and is specific enough for team history scanning.
Linked Issues Check ✅ Passed The changes address the core coding objectives of [#2120]: dependency upgrade to @apollo/client v4, codemod-driven import and HttpLink updates, replacement of legacy query constants with generated TypedDocumentNode documents, and corresponding test and codegen updates; these are the primary migration tasks requested in the linked issue.
Description Check ✅ Passed The PR description references issue #2120 and concisely lists the migration steps (upgrade to @apollo/client v4, run codemod, replace queries with generated typed-document-node, adjust types/enums, and update tests), so it is on-topic and related to the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rudransh-shrivastava
Copy link
Collaborator Author

rudransh-shrivastava commented Sep 3, 2025

Tests will fail.
I will update them once I am done migrating the code.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (10)
frontend/src/components/ModuleForm.tsx (1)

272-285: Fix invalid Error construction and avoid rethrow inside debounced async.

new Error('...', err) is invalid (Error ctor takes a single message). Rethrowing here can cause unhandled promise rejections and noisy UI. Prefer logging + user-facing error state.

Apply:

       try {
         const { data } = await client.query({
           query: SearchProjectNamesDocument,
           variables: { query },
         })
         setRawResults(data.searchProjects || [])
         setShowSuggestions(true)
       } catch (err) {
         setRawResults([])
         setShowSuggestions(false)
-        throw new Error('Error fetching suggestions:', err)
+        console.error('Error fetching suggestions:', err)
+        setError('Failed to fetch project names. Please try again.')
       }
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (1)

125-137: Bug: Spinner masks the 403 view; check denied before showing the loader

Denied users currently see a spinner because the loader return runs first. Show the denied view first, then the loader only when allowed-but-not-ready.

Apply:

-  if (accessStatus === 'checking' || !formData) {
-    return <LoadingSpinner />
-  }
-
-  if (accessStatus === 'denied') {
+  if (accessStatus === 'denied') {
     return (
       <ErrorDisplay
         statusCode={403}
         title="Access Denied"
         message="You do not have permission to edit this module. You will be redirected."
       />
     )
   }
+
+  if (accessStatus === 'checking' || (accessStatus === 'allowed' && !formData)) {
+    return <LoadingSpinner />
+  }
frontend/src/app/my/mentorship/page.tsx (2)

139-146: Resolve the TODO by aligning ProgramCard’s program prop to generated types

The prop is currently a TODO; lock this down to the generated node type (or an adapter). Two options:

  • Update ProgramCard’s prop to accept MyProgram (preferred).
  • Map p to a view-model that matches ProgramCard’s expected shape.

If ProgramCard uses dates, this also depends on fixing the scalar mapping above.

I can adjust ProgramCard’s props and add a simple adapter if needed.


54-62: Use undefined for empty search; consider disabling notifyOnNetworkStatusChange

  • In frontend/src/app/my/mentorship/page.tsx (around line 54), change
    - variables: { search: debouncedQuery, page, limit: 24 },
    + variables: { search: debouncedQuery || undefined, page, limit: 24 },
  • Neither frontend/src/utils/helpers/apolloClient.ts nor frontend/src/server/apolloClient.ts defines a global notifyOnNetworkStatusChange, so add
    notifyOnNetworkStatusChange: false
    to this query’s options to restore v3-style loading behavior.
frontend/src/app/my/mentorship/programs/create/page.tsx (1)

76-85: Harden error handling in catch (narrow unknown to ApolloError/Error).

Avoid relying on any/optional chaining on unknown. Show GraphQL/network details when available.

Apply:

-import { useMutation } from '@apollo/client/react'
+import { useMutation } from '@apollo/client/react'
+import { isApolloError } from '@apollo/client'
@@
-    } catch (err) {
+    } catch (err: unknown) {
+      const message =
+        isApolloError(err)
+          ? err.message
+          : err instanceof Error
+          ? err.message
+          : 'Unable to complete the requested operation.'
       addToast({
-        description: err?.message || 'Unable to complete the requested operation.',
+        description: message,
         title: 'GraphQL Request Failed',
         timeout: 3000,
         shouldShowTimeoutProgress: true,
         color: 'danger',
         variant: 'solid',
       })
frontend/src/wrappers/provider.tsx (1)

27-31: Guard against null Apollo client to prevent runtime crash.

createApolloClient() can return null (see utils/helpers/apolloClient.ts). Passing that into ApolloProvider will throw.

Minimal fix here:

+import { ErrorDisplay } from 'app/global-error'
@@
 export function Providers({ children }: { children: React.ReactNode }) {
   return (
     <Suspense>
       <SessionProvider>
         <HeroUIProvider>
           <NextThemesProvider attribute="class" defaultTheme="dark">
             <ToastProvider />
-            <ApolloProvider client={apolloClient}>
+            {!apolloClient ? (
+              <ErrorDisplay
+                statusCode={500}
+                title="GraphQL client unavailable"
+                message="Missing or invalid GRAPHQL_URL configuration."
+              />
+            ) : (
+              <ApolloProvider client={apolloClient}>
               <AppInitializer />
               {children}
-            </ApolloProvider>
+              </ApolloProvider>
+            )}
           </NextThemesProvider>
         </HeroUIProvider>
       </SessionProvider>
     </Suspense>
   )
 }

Alternative (also valid): make createApolloClient() throw instead of returning null and surface a global error boundary.

frontend/src/utils/helpers/apolloClient.ts (1)

7-11: Avoid returning null from factory; fail fast or provide a clear fallback.

Returning null leaks into consumers (see Providers) and causes crashes if unguarded.

Option A (fail fast):

-  if (!GRAPHQL_URL) {
-    const error = new AppError(500, 'Missing GraphQL URL')
-    handleAppError(error)
-    return null
-  }
+  if (!GRAPHQL_URL) {
+    const error = new AppError(500, 'Missing GraphQL URL')
+    handleAppError(error)
+    throw error
+  }

If you keep returning null, ensure every consumer guards (see suggested Providers fix).

frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1)

29-38: Fix: loading can hang forever when module is missing (return 404).

If the query resolves with getModule === null and no GraphQL error, isLoading never flips to false, so the spinner never stops and the 404 UI never renders.

Apply this minimal patch:

-  useEffect(() => {
-    if (data?.getModule) {
-      setModule(data.getModule)
-      setAdmins(data.getProgram.admins)
-      setIsLoading(false)
-    } else if (error) {
-      handleAppError(error)
-      setIsLoading(false)
-    }
-  }, [data, error])
+  useEffect(() => {
+    if (data) {
+      setModule(data.getModule ?? null)
+      setAdmins(data.getProgram.admins)
+      setIsLoading(false)
+    } else if (error) {
+      handleAppError(error)
+      setIsLoading(false)
+    }
+  }, [data, error])
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1)

29-38: Spinner never stops when module is missing (404 case).

You only clear isLoading when data.getModule exists or when error is set. If the query returns successfully with getModule = null, the spinner persists forever.

Apply this diff:

-  useEffect(() => {
-    if (data?.getModule) {
-      setModule(data.getModule)
-      setAdmins(data.getProgram.admins)
-      setIsLoading(false)
-    } else if (error) {
-      handleAppError(error)
-      setIsLoading(false)
-    }
-  }, [data, error])
+  useEffect(() => {
+    if (data) {
+      setModule(data.getModule)
+      setAdmins(data.getProgram.admins)
+      setIsLoading(false)
+    }
+    if (error) {
+      handleAppError(error)
+      setIsLoading(false)
+    }
+  }, [data, error])

Also applies to: 40-50

frontend/src/app/members/[memberKey]/page.tsx (1)

82-104: Crash when user.bio is empty/undefined.

map is called on possibly undefined (result of user?.bio?.split(...)). This throws at runtime.

Apply this diff:

-  const formattedBio = user?.bio?.split(' ').map((word, index) => {
+  const formattedBio = user?.bio?.split(' ')?.map((word, index) => {

Optionally, default to null to avoid rendering an empty

:

-  const formattedBio = user?.bio?.split(' ')?.map(...)
+  const formattedBio = user?.bio ? user.bio.split(' ').map(...) : null
🧹 Nitpick comments (52)
frontend/src/components/ModuleForm.tsx (1)

272-279: Optional: force fresh search results and guard against cache staleness.

Search suggestions usually benefit from bypassing cache.

Apply:

-        const { data } = await client.query({
+        const { data } = await client.query({
           query: SearchProjectNamesDocument,
           variables: { query },
+          fetchPolicy: 'network-only',
         })
frontend/src/hooks/useDjangoSession.ts (3)

25-28: Nit: coerce shouldSync to a boolean.

As written, shouldSync is boolean | string due to accessToken. Coerce to boolean for clarity.

-    const shouldSync =
-      status === 'authenticated' &&
-      (session as ExtendedSession)?.accessToken &&
-      !sessionStorage.getItem(SYNC_STATUS_KEY)
+    const shouldSync =
+      status === 'authenticated' &&
+      Boolean((session as ExtendedSession)?.accessToken) &&
+      !sessionStorage.getItem(SYNC_STATUS_KEY)

45-49: Avoid possible TS nullability complaint on session.user.

Inside this block session is present, but TS won’t narrow. Assert via cast (or pre-destructure) to keep types happy.

-            update({
-              user: {
-                ...session.user,
-                isOwaspStaff: githubAuth.user?.isOwaspStaff,
-              },
-            }).then(() => {})
+            {
+              const { user } = session as ExtendedSession
+              update({
+                user: {
+                  ...user,
+                  isOwaspStaff: githubAuth.user?.isOwaspStaff,
+                },
+              }).then(() => {})
+            }

50-59: Toast may be lost due to signOut redirect.

signOut() redirects by default, so the toast likely won’t be visible. Either show the toast before signing out, or use redirect: false.

-          } else {
-            signOut() // Invalidate Next.js session if not ok.
-            addToast({
+          } else {
+            addToast({
               color: 'danger',
               description: githubAuth?.message,
               shouldShowTimeoutProgress: true,
               timeout: 4000,
               title: 'Authentication Failed',
               variant: 'bordered',
             })
+            void signOut({ redirect: false }) // Keep user on page so toast is visible.
           }
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (3)

37-70: Avoid orphaned redirect timer; add cleanup in effect

If the component unmounts or dependencies change before the timeout fires, the pending redirect persists. Track and clear the timer.

Apply:

 useEffect(() => {
   if (sessionStatus === 'loading' || queryLoading) {
     return
   }
 
+  let timeoutId: ReturnType<typeof setTimeout> | undefined
+
   if (
     queryError ||
     !data?.getProgram ||
     !data?.getModule ||
     sessionStatus === 'unauthenticated'
   ) {
     setAccessStatus('denied')
     return
   }
 
   const currentUserLogin = (sessionData as ExtendedSession)?.user?.login
   const isAdmin = data.getProgram.admins?.some(
     (admin: { login: string }) => admin.login === currentUserLogin
   )
 
   if (isAdmin) {
     setAccessStatus('allowed')
   } else {
     setAccessStatus('denied')
     addToast({
       title: 'Access Denied',
       description: 'Only program admins can edit modules.',
       color: 'danger',
       variant: 'solid',
       timeout: 4000,
     })
-    setTimeout(() => router.replace(`/my/mentorship/programs/${programKey}`), 1500)
+    timeoutId = setTimeout(
+      () => router.replace(`/my/mentorship/programs/${programKey}`),
+      1500
+    )
   }
-}, [sessionStatus, sessionData, queryLoading, data, programKey, queryError, router])
+  return () => {
+    if (timeoutId) clearTimeout(timeoutId)
+  }
+}, [sessionStatus, sessionData, queryLoading, data, programKey, queryError, router])

81-86: Default projectName to an empty string to keep inputs controlled

Prevents potential uncontrolled-to-controlled input warnings if projectName is undefined.

Apply:

-        projectName: m.projectName,
+        projectName: m.projectName || '',

27-35: Use generated typed documents for this query
Import the code-generated GetProgramAdminsAndModulesDocument and its corresponding GetProgramAdminsAndModulesQuery/GetProgramAdminsAndModulesQueryVariables types from src/types/__generated__/moduleQueries.generated.ts, then replace the legacy GET_PROGRAM_ADMINS_AND_MODULES constant in your useQuery call with the generated document and supply the proper generics. This will ensure full type safety and consistency with the rest of the codebase.

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)

29-30: Avoid duplicating server data in local state; prevent a 404 flicker on refetch

Because you set isRefetching=false before the useQuery data updates, users can briefly hit the 404 branch. Either derive program/modules directly from useQuery or set state from the awaited refetch() result.

Option A (simpler): derive

const { data } = useQuery(...)

const program = data?.getProgram ?? null
const modules = data?.getProgramModules ?? []

Option B (keep state): hydrate from refetch result

const { data: d } = await refetch()
setProgram(d?.getProgram ?? null)
setModules(d?.getProgramModules ?? [])

33-36: Replace TODO with fully typed mutation via codegen

Type the mutation to catch server/variable drift at compile time.

-  // TODO: update type
-  const [updateProgram] = useMutation(UPDATE_PROGRAM_STATUS_MUTATION, {
-    onError: handleAppError,
-  })
+  const [updateProgram] = useMutation<
+    UpdateProgramStatusMutation,
+    UpdateProgramStatusMutationVariables
+  >(UpdateProgramStatusDocument, { onError: handleAppError })

Add the import (outside this hunk):

import {
  UpdateProgramStatusDocument,
  UpdateProgramStatusMutation,
  UpdateProgramStatusMutationVariables,
} from 'types/__generated__/programsMutations.generated'

81-81: Ensure UI waits for refetch before showing success

To avoid toasts firing before the UI updates, wait for refetch completion or use onQueryUpdated.

       await updateProgram({
         variables: {
           inputData: {
             key: program.key,
             name: program.name,
             status: newStatus,
           },
         },
-        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
+        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
+        awaitRefetchQueries: true,
       })

In Apollo Client v4, refetchQueries supports passing DocumentNodes and variables; awaitRefetchQueries is still available, and returning a Promise from onQueryUpdated is the preferred alternative. (apollographql.com)


134-135: Don’t assert dates to string; guard or map scalar to avoid runtime “Invalid date”

Short-term guard (safer if nulls slip through):

-    { label: 'Start Date', value: formatDate(program.startedAt as string) },
-    { label: 'End Date', value: formatDate(program.endedAt as string) },
+    { label: 'Start Date', value: program.startedAt ? formatDate(program.startedAt as unknown as number | string) : 'N/A' },
+    { label: 'End Date', value: program.endedAt ? formatDate(program.endedAt as unknown as number | string) : 'N/A' },

Long-term: map your Date/DateTime scalar in codegen (e.g., DateTime: string) so these casts disappear.

frontend/src/app/my/mentorship/page.tsx (1)

34-34: Introduce a named alias for readability and reuse

The inline indexed access type is correct but hard to scan and reuse.

Apply:

-const [programs, setPrograms] = useState<GetMyProgramsQuery['myPrograms']['programs']>([])
+const [programs, setPrograms] = useState<MyProgram[]>([])

Add near the imports:

type MyProgram = GetMyProgramsQuery['myPrograms']['programs'][number]
frontend/src/app/page.tsx (1)

49-52: Tighten types and loading checks (optional).

  • State typing: allow null in the state to match the initial value.
  • Loading gate: consider waiting on Algolia data too to avoid map flashing.

Apply:

-  const [data, setData] = useState<MainPageData>(null)
+  const [data, setData] = useState<MainPageData | null>(null)

-  if (isLoading || !graphQLData || !geoLocData) {
+  if (isLoading || !graphQLData || geoLocData.length === 0) {
     return <LoadingSpinner />
   }

Also applies to: 94-96

frontend/src/components/CardDetailsPage.tsx (1)

38-38: Convert the TODO into a concrete typing task.

Two low-risk wins:

  • Narrow prop types on SocialLinks.
  • Ensure props with .length (e.g., healthMetricsData) are typed as arrays.

Apply:

-export const SocialLinks = ({ urls }) => {
+export const SocialLinks = ({ urls }: { urls: string[] }) => {

If you’d like, I can open a follow-up issue to “type tighten” DetailsCardProps and children.

frontend/src/hooks/useLogout.ts (1)

1-1: Import path is correct; consider using useApolloClient for cache ops.

Sticking to the React entrypoint is right. For cache clearing, AC v4 recommends using the client directly via useApolloClient() for imperative work, which keeps the mutation tuple focused on UI state. Use clearStore() (no refetch) or resetStore() (refetch active queries) per your UX. (apollographql.com)

Apply:

-import { useMutation } from '@apollo/client/react'
+import { useMutation, useApolloClient } from '@apollo/client/react'
@@
 export const useLogout = () => {
-  const [logoutUser, { loading, client }] = useMutation(LOGOUT_DJANGO_MUTATION)
+  const [logoutUser, { loading }] = useMutation(LOGOUT_DJANGO_MUTATION)
+  const client = useApolloClient()
@@
-      await client.clearStore() // Removes Apollo cache
+      await client.clearStore() // Removes Apollo cache (no refetch)

Note: per past learnings, some call sites intentionally invoke logout() and signOut() both—don’t dedupe unless you plan to adjust those call sites too.

frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx (1)

2-2: Correct migration: import hooks from @apollo/client/react.

Matches AC v4 guidance. If/when you switch this page to codegen TypedDocumentNodes, AC v4 will infer variable/result types from the document directly. (apollographql.com)

If you want, I can prep a follow-up patch to replace CREATE_MODULE/GET_PROGRAM_ADMIN_DETAILS with generated ...Document artifacts for stronger typing.

frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (2)

70-75: Use an anchor for external URLs (accessibility + expected behavior).

Next.js Link is intended for internal routes.

-        <Link href={repository.url} className="text-blue-400 hover:underline">
-          {repository.url}
-        </Link>
+        <a
+          href={repository.url}
+          className="text-blue-400 hover:underline"
+          target="_blank"
+          rel="noopener noreferrer"
+        >
+          {repository.url}
+        </a>

30-41: Tidy useEffect deps.

repositoryKey isn’t referenced inside the effect; either remove it or include organizationKey too for symmetry.

-  }, [data, graphQLRequestError, repositoryKey])
+  }, [data, graphQLRequestError])
frontend/src/utils/helpers/apolloClient.ts (1)

18-27: Optional: centralize GraphQL error logging with an errorLink.

Helps unify toasts/logging instead of per-call try/catch.

+import { onError } from '@apollo/client/link/error'
@@
-  const authLink = setContext(async (_, { headers }) => {
+  const authLink = setContext(async (_, { headers }) => {
     const csrfToken = await getCsrfToken()
@@
   })
+
+  const errorLink = onError(({ graphQLErrors, networkError }) => {
+    if (graphQLErrors?.length || networkError) {
+      handleAppError(new AppError(500, 'GraphQL Request Failed'))
+    }
+  })
@@
-  return new ApolloClient({
+  return new ApolloClient({
     cache: new InMemoryCache(),
-    link: authLink.concat(httpLink),
+    link: errorLink.concat(authLink).concat(httpLink),
   })
frontend/src/app/chapters/[chapterKey]/page.tsx (1)

70-70: Address the TODO comment.

The TODO indicates a type issue with the geolocationData prop.

Would you like me to help resolve the type issue with the geolocationData prop in the DetailsCard component?

frontend/src/app/about/page.tsx (2)

68-73: Reduce state duplication — derive UI straight from query results.

projectMetadata and topContributors mirror data and require effects to sync. Prefer reading projectMetadataResponse?.project and topContributorsResponse?.topContributors directly to avoid extra renders and stale state.

If you want, I can push a minimal diff that inlines these and drops both effects.

Also applies to: 75-94, 101-113


273-275: Tighten type: user is non-null here.

After the null-guard above, make the parameter non-null to prevent accidental misuse elsewhere.

-  const handleButtonClick = (user: GetLeaderDataQuery['user']) => {
+  const handleButtonClick = (user: NonNullable<GetLeaderDataQuery['user']>) => {
     router.push(`/members/${user.login}`)
   }
frontend/src/app/organizations/[organizationKey]/page.tsx (2)

14-14: Add strong types for local state from codegen.

State is any-ish as null. Use GetOrganizationDataQuery to keep props/consumers type-safe.

-import { GetOrganizationDataDocument } from 'types/__generated__/organizationQueries.generated'
+import {
+  GetOrganizationDataDocument,
+} from 'types/__generated__/organizationQueries.generated'
+import type { GetOrganizationDataQuery } from 'types/__generated__/organizationQueries.generated'

-  const [organization, setOrganization] = useState(null)
-  const [issues, setIssues] = useState(null)
-  const [milestones, setMilestones] = useState(null)
-  const [pullRequests, setPullRequests] = useState(null)
-  const [releases, setReleases] = useState(null)
-  const [repositories, setRepositories] = useState(null)
-  const [topContributors, setTopContributors] = useState(null)
+  const [organization, setOrganization] =
+    useState<GetOrganizationDataQuery['organization'] | null>(null)
+  const [issues, setIssues] =
+    useState<GetOrganizationDataQuery['recentIssues'] | null>(null)
+  const [milestones, setMilestones] =
+    useState<GetOrganizationDataQuery['recentMilestones'] | null>(null)
+  const [pullRequests, setPullRequests] =
+    useState<GetOrganizationDataQuery['recentPullRequests'] | null>(null)
+  const [releases, setReleases] =
+    useState<GetOrganizationDataQuery['recentReleases'] | null>(null)
+  const [repositories, setRepositories] =
+    useState<GetOrganizationDataQuery['repositories'] | null>(null)
+  const [topContributors, setTopContributors] =
+    useState<GetOrganizationDataQuery['topContributors'] | null>(null)

Also applies to: 20-27


28-30: Optional: rely on useQuery’s loading instead of a local isLoading state.

This simplifies logic and prevents edge cases where local state and hook state drift.

I can provide a follow-up diff if you want this simplified.

Also applies to: 32-47, 49-54

frontend/src/app/snapshots/[id]/page.tsx (2)

169-172: Keys and nullable dates.

  • Use a stable key (release.id) instead of tagName-index.
  • Guard publishedAt (nullable) to avoid “Released: ” with empty date.
-            {snapshot.newReleases.map((release, index) => (
+            {snapshot.newReleases.map((release) => (
               <div
-                key={`${release.tagName}-${index}`}
+                key={release.id}
                 className="overflow-hidden rounded-lg border-1 border-gray-200 bg-white shadow-xs transition-all hover:shadow-md dark:border-gray-700 dark:bg-gray-800"
               >
...
-                    Released: {formatDate(release.publishedAt as string)}
+                    Released:{' '}
+                    {release.publishedAt
+                      ? formatDate(release.publishedAt as string)
+                      : 'TBD'}

Also applies to: 190-191


13-15: Unify types with codegen to drop casts and TODOs.

renderProjectCard/renderChapterCard take app-local types while you pass codegen objects; plus you cast scalars to string. Prefer codegen types for the render helpers.

-import type { Chapter } from 'types/chapter'
-import type { Project } from 'types/project'
+// Use codegen types to match the query payload
+type SnapshotProject = NonNullable<GetSnapshotDetailsQuery['snapshot']>['newProjects'][number]
+type SnapshotChapter = NonNullable<GetSnapshotDetailsQuery['snapshot']>['newChapters'][number]

-  const renderProjectCard = (project: Project) => {
+  const renderProjectCard = (project: SnapshotProject) => {
...
-  const renderChapterCard = (chapter: Chapter) => {
+  const renderChapterCard = (chapter: SnapshotChapter) => {

Consider mapping GraphQL scalars in codegen (e.g., DateTime -> string) to eliminate as string casts project-wide.

Also applies to: 43-69, 71-97, 153-163

frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)

17-17: Prefer casting over a generic for useParams (Next.js types aren’t generic).

Keeps consistency with other files and avoids type errors across Next versions.

-  const { programKey, moduleKey } = useParams<{ programKey: string; moduleKey: string }>()
+  const { programKey, moduleKey } = useParams() as { programKey: string; moduleKey: string }

54-59: Reduce “as string” casts by mapping date scalars in codegen.

startedAt/endedAt are unknown due to codegen config. Map your GraphQL Date/DateTime scalars to string in codegen to restore type-safety and drop the casts here.

Example (codegen.yml):

config:
  scalars:
    DateTime: string
    Date: string

Then rerun codegen and remove the as string casts at call sites.


19-41: Optional: derive UI state from Apollo instead of mirroring it.

You can drop local module/admins/isLoading state and use const { data, error, loading } = useQuery(...), then read from data directly. Less state, fewer effects, fewer edge cases.

frontend/src/app/snapshots/page.tsx (1)

16-26: Optional: use Apollo’s loading and avoid local isLoading/effect bookkeeping.

Simplifies control flow and removes a class of race conditions around manual loading flags.

-  const [snapshots, setSnapshots] = useState<GetCommunitySnapshotsQuery['snapshots'] | null>(null)
-  const [isLoading, setIsLoading] = useState<boolean>(true)
-
-  const { data: graphQLData, error: graphQLRequestError } = useQuery(GetCommunitySnapshotsDocument)
+  const { data: graphQLData, error: graphQLRequestError, loading } =
+    useQuery(GetCommunitySnapshotsDocument)
+  const snapshots = graphQLData?.snapshots ?? null
@@
-    if (graphQLData) {
-      setSnapshots(graphQLData.snapshots)
-      setIsLoading(false)
-    }
     if (graphQLRequestError) {
       addToast({
@@
-      setIsLoading(false)
     }
   }, [graphQLData, graphQLRequestError])
@@
-  if (isLoading) {
+  if (loading) {
     return <LoadingSpinner />
   }

Also applies to: 63-66

frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (2)

77-79: Drop as string casts by mapping date scalars in codegen.

Same rationale as other files; makes formatDateForInput calls type-safe.

-        startedAt: formatDateForInput(program.startedAt as string),
-        endedAt: formatDateForInput(program.endedAt as string),
+        startedAt: formatDateForInput(program.startedAt),
+        endedAt: formatDateForInput(program.endedAt),

(After adding DateTime: string mapping in codegen.)


19-20: Consistency nit: match useParams typing style used across pages.

Here you cast; in modules page you used a generic. Pick one (casting is safer with Next types) to avoid drift.

frontend/src/app/projects/[projectKey]/page.tsx (2)

21-21: Prefer Apollo’s loading state over bespoke isLoading.

Leverage the loading flag from useQuery to avoid manual state bookkeeping and edge cases on param changes.

Apply this diff:

-  const [isLoading, setIsLoading] = useState<boolean>(true)
+  // Rely on Apollo's loading flag

   const { data, error: graphQLRequestError } = useQuery(GetProjectDocument, {
     variables: { key: projectKey },
   })

   useEffect(() => {
     if (data) {
       setProject(data.project)
       setTopContributors(data.topContributors)
-      setIsLoading(false)
     }
     if (graphQLRequestError) {
       handleAppError(graphQLRequestError)
-      setIsLoading(false)
     }
   }, [data, graphQLRequestError, projectKey])

-  if (isLoading) {
+  // Show spinner while the query is loading
+  if (!project && !graphQLRequestError) {
     return <LoadingSpinner />
   }

Also applies to: 27-37, 39-41


27-37: Unnecessary dependency.

projectKey in the effect dependencies isn’t used inside the effect and will cause extra runs on param changes.

-  }, [data, graphQLRequestError, projectKey])
+  }, [data, graphQLRequestError])
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)

19-19: Type admins state explicitly.

Avoid implicit any; align with generated types.

-  const [admins, setAdmins] = useState(null)
+  const [admins, setAdmins] = useState<
+    GetProgramAdminsAndModulesQuery['getProgram']['admins']
+  >(null)

22-27: Use Apollo loading instead of manual isLoading.

Same suggestion as other pages for consistency and fewer states.

-  const [isLoading, setIsLoading] = useState(true)
-  const { data, error } = useQuery(GetProgramAdminsAndModulesDocument, {
+  const { data, error, loading } = useQuery(GetProgramAdminsAndModulesDocument, {
     variables: { programKey, moduleKey },
   })
@@
-  if (isLoading) return <LoadingSpinner />
+  if (loading) return <LoadingSpinner />

Also applies to: 40-41

frontend/src/app/members/[memberKey]/page.tsx (1)

23-31: Minor state/flow cleanups.

  • Initialize user to null to avoid undefined in unions.
  • Prefer Apollo loading flag over manual isLoading for consistency.
-  const [user, setUser] = useState<GetUserDataQuery['user'] | null>()
+  const [user, setUser] = useState<GetUserDataQuery['user'] | null>(null)
@@
-  const [isLoading, setIsLoading] = useState<boolean>(true)
+  // Rely on Apollo 'loading'
@@
-  const { data: graphQLData, error: graphQLRequestError } = useQuery(GetUserDataDocument, {
+  const { data: graphQLData, error: graphQLRequestError, loading } = useQuery(GetUserDataDocument, {
     variables: { key: memberKey },
   })
@@
-  if (isLoading) {
+  if (loading) {
     return <LoadingSpinner />
   }
@@
-  if (!isLoading && user == null) {
+  if (!loading && user == null) {
     return (

Also applies to: 106-118, 211-225

frontend/src/app/settings/api-keys/page.tsx (10)

37-37: Small type alias to reduce repetition

Define an alias for readability and reuse.

-const [keyToRevoke, setKeyToRevoke] = useState<GetApiKeysQuery['apiKeys'][0] | null>(null)
+type ApiKey = GetApiKeysQuery['apiKeys'][number]
+const [keyToRevoke, setKeyToRevoke] = useState<ApiKey | null>(null)

39-42: Don’t block on errors when errorPolicy='all'

With errorPolicy: 'all', you may have both data and error; current ternary hides partial data. Gate the error block on !data.

-          {error ? (
+          {error && !data ? (
             <div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
               Error loading API keys
             </div>
           ) : loading ? (

Optionally surface a non-blocking banner when error && data.

Also applies to: 217-229


105-114: Validate expiry and send ISO string to avoid TZ ambiguity

Ensure date isn’t in the past and serialize as ISO (servers usually expect strings).

-  const variables: { name: string; expiresAt: Date } = {
-    name: newKeyName.trim(),
-    expiresAt: new Date(newKeyExpiry),
-  }
-  createApiKey({ variables })
+  const expiry = new Date(newKeyExpiry)
+  const today = new Date(); today.setHours(0,0,0,0)
+  if (isNaN(expiry.getTime()) || expiry < today) {
+    addToast({ title: 'Error', description: 'Expiration must be today or later', color: 'danger' })
+    return
+  }
+  createApiKey({
+    variables: { name: newKeyName.trim(), expiresAt: expiry.toISOString() },
+  })

Consider mapping scalars in codegen so DateTime/UUID are typed as strings (see separate comment).


68-76: Disable revoke button while mutation is in flight

Prevents double submissions and accidental repeats.

-const [revokeApiKey] = useMutation(RevokeApiKeyDocument, {
+const [revokeApiKey, { loading: revokeLoading }] = useMutation(RevokeApiKeyDocument, {
   onCompleted: () => {
-            <Button color="danger" onPress={handleRevokeKey}>
+            <Button color="danger" onPress={handleRevokeKey} isDisabled={revokeLoading}>
               Revoke Key
             </Button>

Also applies to: 430-432


80-81: Compute default expiry on open to avoid stale “30 days from today”

Minor UX nit if the page stays open across midnight.

-const defaultExpiryDate = format(addDays(new Date(), 30), 'yyyy-MM-dd')
...
-  setNewKeyExpiry(defaultExpiryDate)
+  setNewKeyExpiry(format(addDays(new Date(), 30), 'yyyy-MM-dd'))

Also applies to: 137-140


242-252: Prefer stable string types and avoid casts; consider codegen scalar mapping

Use id (string) for React key, and coerce with String(...) over as-casts. Also, map GraphQL scalars to real TS types to remove unknowns.

-{data.apiKeys.map((key: GetApiKeysQuery['apiKeys'][0]) => (
+{data.apiKeys.map((key: GetApiKeysQuery['apiKeys'][number]) => (
   <tr
-    key={key.uuid as string}
+    key={key.id}
     className="border-b border-b-gray-200 dark:border-b-gray-700"
   >
     <td className="py-3">{key.name}</td>
-    <td className="py-3 font-mono text-sm">{key.uuid as string}</td>
-    <td className="py-3">{format(new Date(key.createdAt as string), 'PP')}</td>
+    <td className="py-3 font-mono text-sm">{String(key.uuid)}</td>
+    <td className="py-3">{format(new Date(String(key.createdAt)), 'PP')}</td>
     <td className="py-3">
-      {key.expiresAt ? format(new Date(key.expiresAt as string), 'PP') : 'Never'}
+      {key.expiresAt ? format(new Date(String(key.expiresAt)), 'PP') : 'Never'}
     </td>

Codegen (typescript plugin) suggestion to remove unknowns:

# codegen.yml
generates:
  frontend/src/types/__generated__/:
    plugins:
      - typescript
      - typescript-operations
    config:
      scalars:
        DateTime:
          input: string
          output: string
        UUID:
          input: string
          output: string

234-234: Tailwind class nit: 'border-b-1' isn’t a default utility

Use 'border-b' or an arbitrary value 'border-b-[1px]'.

-<tr className="border-b-1 border-b-gray-200 dark:border-b-gray-700">
+<tr className="border-b border-b-gray-200 dark:border-b-gray-700">

192-195: Wording: use “revoke” to match the action

Align the guidance with the button/action label.

-You've reached the maximum number of API keys. Delete an existing key to create
+You've reached the maximum number of API keys. Revoke an existing key to create

150-158: Type safety when passing uuid to mutation

uuid is typed unknown in generated types; explicit string coercion avoids TS complaints and accidental non-string values.

-        await revokeApiKey({ variables: { uuid: keyToRevoke.uuid } })
+        await revokeApiKey({ variables: { uuid: String(keyToRevoke.uuid) } })

248-251: Potential date off-by-one in some timezones

If backend stores midnight UTC, local formatting can show the prior day. Consider storing/displaying local midnight or using date-fns-tz’s formatInTimeZone to render in UTC.

Would you like a patch using date-fns-tz (formatInTimeZone) to render consistently?

frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)

42-52: Optional: avoid mirroring server data in local state

You can render directly from data?.project and drop the useEffect + two useStates to reduce re-renders and state sync risks. Happy to draft a refactor if you want.


58-64: Guard scalar parsing instead of unknownstring cast

createdAt is unknown (custom scalar). Prefer safe coercion or map the scalar to string in codegen.

-  const labels =
-    metricsList?.map((m) =>
-      new Date(m.createdAt as string).toLocaleString('default', {
+  const labels =
+    metricsList?.map(({ createdAt }) =>
+      new Date(String(createdAt)).toLocaleString('default', {
         month: 'short',
         day: 'numeric',
       })
     ) || []

Alternatively, map your GraphQL Date/DateTime scalar to string in codegen.

frontend/src/app/projects/dashboard/metrics/page.tsx (3)

89-93: Nit: remove redundant || fallback

currentOrdering is always truthy. Drop the || to simplify.

-  const [ordering, setOrdering] = useState(
-    currentOrdering || {
-      score: Ordering.Desc,
-    }
-  )
+  const [ordering, setOrdering] = useState(currentOrdering)

98-115: Consider explicit error handling policy with v4 defaults

In v4, notifyOnNetworkStatusChange defaults to true, and error handling semantics are unified. If you expect partial data alongside errors, set errorPolicy: 'all'. Otherwise, you may see more loading transitions. (apollographql.com)

   } = useQuery(GetProjectHealthMetricsDocument, {
     variables: {
       filters,
       pagination: { offset: 0, limit: PAGINATION_LIMIT },
       ordering: [
         ordering,
         {
           // eslint-disable-next-line @typescript-eslint/naming-convention
           project_Name: Ordering.Asc,
         },
       ],
     },
+    // Optional: opt into partial data + errors
+    // errorPolicy: 'all',
   })

244-245: Follow-up: update MetricsCard prop types, remove TODO

Define metric as the generated item type to get full type coverage.

Example (in MetricsCard.tsx):

import type { GetProjectHealthMetricsQuery } from 'types/__generated__/projectsHealthDashboardQueries.generated'
type MetricT = GetProjectHealthMetricsQuery['projectHealthMetrics'][number];

export interface MetricsCardProps {
  metric: MetricT;
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6092fe and b8a110c.

⛔ Files ignored due to path filters (2)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • frontend/src/types/__generated__/userQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (32)
  • frontend/package.json (2 hunks)
  • frontend/src/app/about/page.tsx (6 hunks)
  • frontend/src/app/chapters/[chapterKey]/page.tsx (2 hunks)
  • frontend/src/app/committees/[committeeKey]/page.tsx (2 hunks)
  • frontend/src/app/members/[memberKey]/page.tsx (3 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/page.tsx (5 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (1 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx (1 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5 hunks)
  • frontend/src/app/my/mentorship/programs/create/page.tsx (1 hunks)
  • frontend/src/app/organizations/[organizationKey]/page.tsx (3 hunks)
  • frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (1 hunks)
  • frontend/src/app/page.tsx (1 hunks)
  • frontend/src/app/projects/[projectKey]/page.tsx (2 hunks)
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (4 hunks)
  • frontend/src/app/projects/dashboard/metrics/page.tsx (6 hunks)
  • frontend/src/app/projects/dashboard/page.tsx (3 hunks)
  • frontend/src/app/settings/api-keys/page.tsx (5 hunks)
  • frontend/src/app/snapshots/[id]/page.tsx (5 hunks)
  • frontend/src/app/snapshots/page.tsx (2 hunks)
  • frontend/src/components/CardDetailsPage.tsx (1 hunks)
  • frontend/src/components/ModuleForm.tsx (2 hunks)
  • frontend/src/hooks/useDjangoSession.ts (2 hunks)
  • frontend/src/hooks/useLogout.ts (1 hunks)
  • frontend/src/server/apolloClient.ts (2 hunks)
  • frontend/src/server/queries/userQueries.ts (1 hunks)
  • frontend/src/utils/helpers/apolloClient.ts (2 hunks)
  • frontend/src/wrappers/provider.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/chapters/[chapterKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/projects/[projectKey]/page.tsx
  • frontend/src/app/organizations/[organizationKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/organizations/[organizationKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/components/ModuleForm.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-11T15:46:58.233Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/UserMenu.tsx:56-60
Timestamp: 2025-07-11T15:46:58.233Z
Learning: In the UserMenu component's handleLogout function, both logout() and signOut() calls are intentionally kept despite the useLogout hook already calling signOut internally, as this is necessary for the specific implementation requirements.

Applied to files:

  • frontend/src/hooks/useLogout.ts
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
📚 Learning: 2025-07-12T17:14:28.536Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/[programKey]/edit/page.tsx:90-128
Timestamp: 2025-07-12T17:14:28.536Z
Learning: Both ProgramForm (programCard.tsx) and ModuleForm (mainmoduleCard.tsx) components already implement HTML validation using the `required` attribute on form fields. The browser's native validation prevents form submission and displays error messages when required fields are empty, eliminating the need for additional JavaScript validation before GraphQL mutations.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
  • frontend/src/app/projects/dashboard/metrics/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
  • frontend/src/app/projects/dashboard/metrics/page.tsx
  • frontend/src/app/projects/dashboard/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.

Applied to files:

  • frontend/src/app/members/[memberKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (20)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
  • GetProgramAndModulesDocument (37-37)
  • GetProgramAndModulesQuery (25-25)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/components/ModuleForm.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (1)
  • SearchProjectNamesDocument (39-39)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
  • GetProjectHealthMetricsDetailsQuery (23-23)
  • GetProjectHealthMetricsDetailsDocument (28-28)
frontend/src/app/settings/api-keys/page.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (4)
  • GetApiKeysQuery (7-7)
  • GetApiKeysDocument (25-25)
  • CreateApiKeyDocument (26-26)
  • RevokeApiKeyDocument (27-27)
frontend/src/hooks/useDjangoSession.ts (1)
frontend/src/types/__generated__/authQueries.generated.ts (1)
  • SyncDjangoSessionDocument (18-18)
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetProgramDetailsDocument (36-36)
frontend/src/utils/dateFormatter.ts (1)
  • formatDateForInput (63-70)
frontend/src/app/members/[memberKey]/page.tsx (1)
frontend/src/types/__generated__/userQueries.generated.ts (2)
  • GetUserDataQuery (16-16)
  • GetUserDataDocument (27-27)
frontend/src/app/projects/dashboard/metrics/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
  • GetProjectHealthMetricsQuery (16-16)
  • GetProjectHealthMetricsDocument (27-27)
frontend/src/app/about/page.tsx (2)
frontend/src/types/__generated__/projectQueries.generated.ts (4)
  • GetProjectMetadataDocument (37-37)
  • GetTopContributorsDocument (38-38)
  • GetProjectMetadataQuery (16-16)
  • GetTopContributorsQuery (26-26)
frontend/src/types/__generated__/userQueries.generated.ts (2)
  • GetLeaderDataDocument (26-26)
  • GetLeaderDataQuery (9-9)
frontend/src/app/chapters/[chapterKey]/page.tsx (2)
frontend/src/types/__generated__/chapterQueries.generated.ts (2)
  • GetChapterDataQuery (9-9)
  • GetChapterDataDocument (19-19)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
frontend/src/types/__generated__/moduleQueries.generated.ts (2)
  • GetProgramAdminsAndModulesQuery (25-25)
  • GetProgramAdminsAndModulesDocument (30-30)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/components/ModuleCard.tsx (1)
  • getSimpleDuration (98-110)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
  • GetSnapshotDetailsQuery (9-9)
  • GetSnapshotDetailsDocument (24-24)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/app/my/mentorship/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
  • GetMyProgramsQuery (11-11)
  • GetMyProgramsDocument (35-35)
frontend/src/app/projects/dashboard/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
  • GetProjectHealthStatsQuery (7-7)
  • GetProjectHealthStatsDocument (26-26)
frontend/src/app/snapshots/page.tsx (2)
backend/apps/owasp/api/internal/queries/snapshot.py (2)
  • snapshots (25-31)
  • snapshot (14-22)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
  • GetCommunitySnapshotsQuery (21-21)
  • GetCommunitySnapshotsDocument (26-26)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
  • GetProgramAndModulesQuery (25-25)
  • GetProgramAndModulesDocument (37-37)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/app/projects/[projectKey]/page.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
  • GetProjectQuery (9-9)
  • GetProjectDocument (36-36)
frontend/src/app/committees/[committeeKey]/page.tsx (3)
backend/apps/owasp/api/internal/queries/committee.py (1)
  • committee (14-28)
frontend/src/types/__generated__/committeeQueries.generated.ts (2)
  • GetCommitteeDataQuery (9-9)
  • GetCommitteeDataDocument (19-19)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/app/organizations/[organizationKey]/page.tsx (1)
frontend/src/types/__generated__/organizationQueries.generated.ts (1)
  • GetOrganizationDataDocument (19-19)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
frontend/src/types/__generated__/moduleQueries.generated.ts (2)
  • GetProgramAdminsAndModulesQuery (25-25)
  • GetProgramAdminsAndModulesDocument (30-30)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/components/ModuleCard.tsx (1)
  • getSimpleDuration (98-110)
🪛 Biome (2.1.2)
frontend/src/app/snapshots/[id]/page.tsx

[error] 147-147: Wrap comments inside children within braces.

Unsafe fix: Wrap the comments with braces

(lint/suspicious/noCommentText)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (2)
frontend/src/app/snapshots/page.tsx (1)

40-42: Prefer a readable item alias and let map infer the element type.

Use a SnapshotItem alias ([number] index) and drop the explicit element type in map. This mirrors prior feedback.

Apply this diff:

-  const handleButtonClick = (snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => {
+  const handleButtonClick = (snapshot: SnapshotItem) => {
     router.push(`/snapshots/${snapshot.key}`)
   }
-  const renderSnapshotCard = (snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => {
+  const renderSnapshotCard = (snapshot: SnapshotItem) => {
-            snapshots.map((snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => (
+            snapshots.map((snapshot) => (
               <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div>
             ))

Add this alias near the imports (outside the diffed ranges):

type SnapshotItem = GetCommunitySnapshotsQuery['snapshots'][number]

Also applies to: 44-50, 73-75

frontend/src/app/snapshots/[id]/page.tsx (1)

144-145: Remove stray whitespace text node to satisfy Biome rule.

This is the same issue previously flagged; the {' '} child is unnecessary here.

-            {' '}
             {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}
🧹 Nitpick comments (14)
frontend/src/utils/utility.ts (2)

7-7: Use type-only import to avoid emitting runtime code.

Switch to a type import for the generated GraphQL type.

-import { GetSnapshotDetailsQuery } from 'types/__generated__/snapshotQueries.generated'
+import type { GetSnapshotDetailsQuery } from 'types/__generated__/snapshotQueries.generated'

29-36: Harden createdAt handling and tighten types for params/reduce accumulator.

  • Accept only known icon keys in params.
  • Support both numeric (sec/ms) and string timestamps for createdAt.
  • Type the reducer accumulator as Partial<Icon> to avoid {}-as-Icon casts leaking.
-export const getFilteredIcons = (project: ProjectType, params: string[]): Icon => {
-  const filteredIcons = params.reduce((acc: Icon, key) => {
+export const getFilteredIcons = (project: ProjectType, params: readonly IconKeys[]): Icon => {
+  const filteredIcons = params.reduce<Partial<Icon>>((acc, key) => {
     if (ICONS[key as IconKeys] && project[key as keyof typeof project] !== undefined) {
       if (key === 'createdAt') {
-        acc[key] = dayjs.unix(project[key as keyof ProjectType] as unknown as number).fromNow()
+        const raw = project[key as keyof ProjectType] as unknown
+        const d =
+          typeof raw === 'number'
+            ? (raw > 1_000_000_000_000 ? dayjs(raw) : dayjs.unix(raw))
+            : dayjs(raw as string)
+        if (d.isValid()) acc[key] = d.fromNow()
       } else {
-        acc[key] = project[key as keyof typeof project] as unknown as number
+        acc[key] = project[key as keyof ProjectType] as unknown as number
       }
     }
     return acc
-  }, {})
+  }, {})
 
-  return filteredIcons
+  return filteredIcons as Icon
 }

Also applies to: 41-41

frontend/src/app/snapshots/page.tsx (2)

15-19: Remove mirrored state; rely on Apollo’s loading/data to simplify and avoid effect churn.

isLoading and snapshots duplicate useQuery state and add unnecessary effects (can also double-fire toasts under React StrictMode). Derive from useQuery instead.

Apply this diff:

-  const [snapshots, setSnapshots] = useState<GetCommunitySnapshotsQuery['snapshots'] | null>(null)
-  const [isLoading, setIsLoading] = useState<boolean>(true)
-
-  const { data: graphQLData, error: graphQLRequestError } = useQuery(GetCommunitySnapshotsDocument)
+  const { data, error, loading } = useQuery(GetCommunitySnapshotsDocument)
+  const snapshots = data?.snapshots ?? []
-  useEffect(() => {
-    if (graphQLData) {
-      setSnapshots(graphQLData.snapshots)
-      setIsLoading(false)
-    }
-    if (graphQLRequestError) {
-      addToast({
-        description: 'Unable to complete the requested operation.',
-        title: 'GraphQL Request Failed',
-        timeout: 3000,
-        shouldShowTimeoutProgress: true,
-        color: 'danger',
-        variant: 'solid',
-      })
-      setIsLoading(false)
-    }
-  }, [graphQLData, graphQLRequestError])
+  useEffect(() => {
+    if (error) {
+      addToast({
+        description: 'Unable to complete the requested operation.',
+        title: 'GraphQL Request Failed',
+        timeout: 3000,
+        shouldShowTimeoutProgress: true,
+        color: 'danger',
+        variant: 'solid',
+      })
+    }
+  }, [error])
-  if (isLoading) {
+  if (loading) {
     return <LoadingSpinner />
   }
-          {!snapshots?.length ? (
+          {!snapshots.length ? (
             <div className="col-span-full py-8 text-center">No Snapshots found</div>
           ) : (
             snapshots.map((snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => (
               <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div>
             ))
           )}

Additionally, remove useState from the React import:

- import React, { useState, useEffect } from 'react'
+ import React, { useEffect } from 'react'

Also applies to: 20-36, 62-64, 70-76


53-53: Avoid double keys and an extra wrapper node.

You set key on <SnapshotCard> and also on the wrapping <div>. Keep one. Easiest: remove the wrapper and map directly to the card-returning renderer.

Apply this diff:

-            snapshots.map((snapshot) => (
-              <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div>
-            ))
+            snapshots.map(renderSnapshotCard)

Also applies to: 73-75

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)

29-31: Avoid mirroring Apollo data in local state.

Storing program/modules in state and syncing via useEffect risks staleness and extra renders. Prefer deriving directly from data.

Example (outside this hunk): after the useQuery(...) call, derive:

const program = data?.getProgram ?? null
const modules = data?.getProgramModules ?? []

Then remove the setProgram/setModules calls in the effect.


41-45: Consider cache-and-network for snappier UX.

If acceptable, fetch from cache first while updating in background.

   } = useQuery(GetProgramAndModulesDocument, {
     variables: { programKey },
     skip: !programKey,
     notifyOnNetworkStatusChange: true,
+    fetchPolicy: 'cache-and-network',
   })

144-144: Resolve TODO: type modules prop and align accessLevel with actual role.

  • Update DetailsCard prop to accept GetProgramAndModulesQuery['getProgramModules'] to drop the TODO.
  • Consider accessLevel={isAdmin ? 'admin' : 'read'} to avoid overexposing actions when the viewer isn’t an admin.

I can open a follow-up to type DetailsCard correctly and thread accessLevel from isAdmin. Want me to draft it?


2-2: Import Apollo hooks from @apollo/client/react everywhere. Test files are still importing hooks (e.g. useQuery, useMutation, useApolloClient) from @apollo/client; update those to the @apollo/client/react subpath to maintain repo-wide consistency and avoid mixed entry points.


71-81: Add awaitRefetchQueries to the mutation call
Ensure the mutation’s returned promise waits for the refetch by adding awaitRefetchQueries: true alongside refetchQueries—the input shape already includes name, so no changes needed there.

frontend/src/app/snapshots/[id]/page.tsx (5)

9-12: Good switch to generated Document and typed query.

This enables end-to-end type inference for useQuery.

If possible, also export and use the generated GetSnapshotDetailsQueryVariables for extra safety on variables.


21-22: Tighten snapshot state type.

GetSnapshotDetailsQuery['snapshot'] already includes null, so the extra "| null" is redundant.

-  const [snapshot, setSnapshot] = useState<GetSnapshotDetailsQuery['snapshot'] | null>(null)
+  const [snapshot, setSnapshot] = useState<GetSnapshotDetailsQuery['snapshot']>(null)

26-28: Guard the query until the route param is available; consider relying on useQuery.loading.

Add skip: !snapshotKey to avoid a spurious request with undefined variables. Optional: favor loading from useQuery over a parallel isLoading state/effect to reduce race conditions.

-  const { data: graphQLData, error: graphQLRequestError } = useQuery(GetSnapshotDetailsDocument, {
-    variables: { key: snapshotKey },
-  })
+  const { data: graphQLData, error: graphQLRequestError } = useQuery(GetSnapshotDetailsDocument, {
+    variables: { key: snapshotKey },
+    skip: !snapshotKey,
+  })

186-186: Handle nullable publishedAt without string cast; prefer a readable fallback.

Also consider using release.id as the React key instead of tag/index (outside this hunk).

-                    Released: {formatDate(release.publishedAt as string)}
+                    Released: {release.publishedAt ? formatDate(release.publishedAt as number | string) : 'TBD'}

Outside this hunk, change the item key to a stable id:

key={release.id}

9-12: Codegen scalars mapping to remove “unknown” dates (one-time config).

Map server date/timestamp scalars to concrete TS types so casts disappear and type-safety improves.

Add to codegen config:

generates:
  frontend/src/types/__generated__/:
    plugins:
      - typescript
      - typescript-operations
    config:
      scalars:
        DateTime: string
        Timestamp: number
        Date: string

Then re-run codegen and replace the remaining as number | string casts.

Also applies to: 123-123, 186-186

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b8a110c and 13c6f52.

📒 Files selected for processing (4)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5 hunks)
  • frontend/src/app/snapshots/[id]/page.tsx (7 hunks)
  • frontend/src/app/snapshots/page.tsx (4 hunks)
  • frontend/src/utils/utility.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (4)
frontend/src/utils/utility.ts (3)
frontend/src/types/project.ts (1)
  • Project (17-45)
frontend/src/types/chapter.ts (1)
  • Chapter (3-19)
frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
  • GetSnapshotDetailsQuery (9-9)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
  • GetSnapshotDetailsQuery (9-9)
  • GetSnapshotDetailsDocument (24-24)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
  • GetProgramAndModulesQuery (25-25)
  • GetProgramAndModulesDocument (37-37)
frontend/src/types/__generated__/programsMutations.generated.ts (1)
  • UpdateProgramStatusDocument (28-28)
frontend/src/app/global-error.tsx (1)
  • handleAppError (66-86)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/app/snapshots/page.tsx (2)
backend/apps/owasp/api/internal/queries/snapshot.py (2)
  • snapshots (25-31)
  • snapshot (14-22)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
  • GetCommunitySnapshotsQuery (21-21)
  • GetCommunitySnapshotsDocument (26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (5)
frontend/src/app/snapshots/page.tsx (2)

2-2: Apollo v4 import path is correct.

Importing useQuery from @apollo/client/react is v4-compliant. No action needed.


7-10: Good move to codegen documents and types.

Using GetCommunitySnapshotsDocument and GetCommunitySnapshotsQuery tightens type-safety.

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)

9-14: Switch to codegen documents/types — LGTM.

Using generated DocumentNodes and enums tightens type-safety and aligns with the migration plan. Also compatible with the prior learning that GET_PROGRAM_DETAILS remains in use elsewhere; no conflict here.


33-35: Mutation hook wiring — LGTM.

onError: handleAppError centralizes error handling; keep it.

frontend/src/app/snapshots/[id]/page.tsx (1)

2-2: Apollo v4 React import looks good.

Using the React-only subpath is compatible with v4 and keeps the bundle lean.

@rudransh-shrivastava rudransh-shrivastava force-pushed the refactor/migrate-apollo-3-to-4 branch from 13c6f52 to b8a110c Compare September 21, 2025 06:49
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/app/snapshots/[id]/layout.tsx (1)

10-16: Incorrect params typing/await in generateMetadata—use plain object.

Next.js passes params as a plain object; remove Promise typing/await to match runtime and typings.

Apply this diff:

 export async function generateMetadata({
   params,
 }: {
-  params: Promise<{ id: string }>
+  params: { id: string }
 }): Promise<Metadata> {
-  const { id: snapshotKey } = await params
+  const { id: snapshotKey } = params
   const { data } = await apolloClient.query({
     query: GetSnapshotDetailsMetadataDocument,
     variables: { key: snapshotKey },
   })
🧹 Nitpick comments (20)
frontend/src/types/chapter.ts (1)

5-18: Avoid blanket-optionalizing Chapter; prefer query-specific/generated types or local Partial/Pick.

Most Chapter fields shouldn't be blanket-optional — keep Chapter strict and either use GraphQL‑codegen selection types per query (Apollo v4) or introduce a derived partial for call sites. Suggested helper: export type ChapterPartial = Partial

frontend/src/types/committee.ts (1)

16-16: Make topContributors required (or explicitly nullable)

GraphQL codegen shows topContributors as a non-null array — prefer a required array and normalise upstream to [] (or use explicit null union if the schema can return null).

Location: frontend/src/types/committee.ts:16

Suggested change:

-  topContributors?: Contributor[]
+  topContributors: Contributor[]  // ensure upstream maps undefined/null to []

Call sites/tests to update: frontend/tests/unit/components/CardDetailsPage.test.tsx:1074 and 1195-1196 (these pass topContributors={undefined}).

frontend/src/types/pullRequest.ts (2)

3-5: Document why author is nullable.

A brief note will prevent churn and clarify expectations for consumers.

Apply:

 export type PullRequest = {
-  author?: User
+  // author may be undefined if not returned by the API or omitted from the selection set
+  author?: User

3-10: Optional: keep domain types strict; narrow at query boundaries.

If this nullability is only due to specific queries, consider mapping API results to a stricter UI/domain type (with a fallback author) at the data layer instead of loosening the shared type. Given the migration focus, feel free to defer.

frontend/src/types/healthMetrics.ts (1)

30-30: Self-document createdAt format (optional)

If this is ISO‑8601, consider a branded alias to improve readability and reduce misuse.

Apply:

+export type ISODateString = string // e.g., 2025-09-22T12:34:56Z
 export type HealthMetricsProps = {
   ...
-  createdAt?: string
+  createdAt?: ISODateString
   ...
 }
frontend/src/types/mentorship.ts (1)

2-3: Import these enums as types; optionally re-export to avoid eslint disable

Generated file exports runtime enums, but this file uses them only in type positions — switch to a type-only import.

Apply this minimal diff:

-// eslint-disable-next-line no-restricted-imports
-import { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'
+// eslint-disable-next-line no-restricted-imports
+import type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'

Optional follow-up (separate change): create frontend/src/types/graphql.ts

// re-export types to avoid importing from __generated__ all over
export type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'

Then update this file:

-// eslint-disable-next-line no-restricted-imports
-import type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'
+import type { ExperienceLevelEnum, ProgramStatusEnum } from 'types/graphql'
frontend/src/types/issue.ts (1)

7-7: Immutable labels: consider ReadonlyArray

Minor: if labels are not mutated, convey intent and help the compiler.

-  labels?: string[]
+  labels?: ReadonlyArray<string>
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (1)

56-56: Date casting should be verified for safety.

The cast of createdAt to string in the date formatting should be verified against the actual data type returned by the GraphQL query, especially given the scalar mapping configuration.

Based on the codegen configuration that maps Date/DateTime to 'string | number', this cast might be unsafe if the backend returns epoch seconds:

-      new Date(m.createdAt as string).toLocaleString('default', {
+      new Date(typeof m.createdAt === 'number' ? m.createdAt * 1000 : m.createdAt as string).toLocaleString('default', {
frontend/src/utils/structuredData.ts (2)

4-15: formatISODate: undefined and ms/sec handling; avoid throwing.

If createdAt/updatedAt are absent or already in milliseconds, current logic can throw or mis-convert. Make the helper accept undefined and auto-detect seconds vs milliseconds; return undefined instead of throwing so callers can omit the field.

Apply this diff:

-export const formatISODate = (input: number | string): string => {
-  const date =
-    typeof input === 'number'
-      ? new Date(input * 1000) // Unix timestamp in seconds
-      : new Date(input) // ISO date string
-
-  if (isNaN(date.getTime())) {
-    throw new Error('Invalid date')
-  }
-
-  return date.toISOString()
-}
+export const formatISODate = (input: number | string | undefined): string | undefined => {
+  if (input == null) return undefined
+  if (typeof input === 'number') {
+    // Heuristic: > 1e12 likely ms; else seconds.
+    const ms = input > 1_000_000_000_000 ? input : input * 1000
+    const d = new Date(ms)
+    return isNaN(d.getTime()) ? undefined : d.toISOString()
+  }
+  const d = new Date(input)
+  return isNaN(d.getTime()) ? undefined : d.toISOString()
+}

58-59: Avoid emitting sameAs: [undefined].

Only include sameAs when URL exists.

Apply this diff:

-      sameAs: [user.url],
+      ...(user.url && { sameAs: [user.url] }),
frontend/src/app/members/[memberKey]/page.tsx (1)

29-29: Initialize user state to null to avoid triple-state (undefined | null | User).

Current call without an initial value makes state possibly undefined and complicates checks.

Apply this diff:

-  const [user, setUser] = useState<User | null>()
+  const [user, setUser] = useState<User | null>(null)
frontend/src/app/settings/api-keys/page.tsx (3)

20-26: Use generated GraphQL types; drop legacy ApiKey type to prevent drift.

Stay consistent with codegen outputs to avoid mismatches and unsafe casts.

Apply this diff:

 import {
   CreateApiKeyDocument,
   GetApiKeysDocument,
   RevokeApiKeyDocument,
 } from 'types/__generated__/apiKeyQueries.generated'
-import { ApiKey } from 'types/apiKey'
+import type { GetApiKeysQuery } from 'types/__generated__/apiKeyQueries.generated'

37-37: Key-to-revoke state should use generated item type.

Aligns state with the query result shape.

Apply this diff:

-  const [keyToRevoke, setKeyToRevoke] = useState<ApiKey | null>(null)
+  const [keyToRevoke, setKeyToRevoke] = useState<GetApiKeysQuery['apiKeys'][0] | null>(null)

242-252: Drop explicit ApiKey annotation and unsafe casts; rely on codegen types.

Remove the manual type on map and string casts—codegen already types uuid/createdAt/expiresAt as strings.

Apply this diff:

-                  {data.apiKeys.map((key: ApiKey) => (
+                  {data.apiKeys.map((key) => (
                     <tr
-                      key={key.uuid as string}
+                      key={key.uuid}
                       className="border-b border-b-gray-200 dark:border-b-gray-700"
                     >
                       <td className="py-3">{key.name}</td>
-                      <td className="py-3 font-mono text-sm">{key.uuid as string}</td>
-                      <td className="py-3">{format(new Date(key.createdAt as string), 'PP')}</td>
+                      <td className="py-3 font-mono text-sm">{key.uuid}</td>
+                      <td className="py-3">{format(new Date(key.createdAt), 'PP')}</td>
                       <td className="py-3">
-                        {key.expiresAt ? format(new Date(key.expiresAt as string), 'PP') : 'Never'}
+                        {key.expiresAt ? format(new Date(key.expiresAt), 'PP') : 'Never'}
                       </td>
frontend/src/app/snapshots/[id]/page.tsx (3)

138-142: Use numeric zIndex.

Style props expect a number; '0' is a string.

Apply:

-              style={{ height: '400px', width: '100%', zIndex: '0' }}
+              style={{ height: '400px', width: '100%', zIndex: 0 }}

145-147: Remove stray text node.

The lone {' '} is noise and can trip linters.

Apply:

-            {' '}
             {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}

12-12: Import SnapshotDetails as a type

tsconfig.json (frontend/tsconfig.json) does not set preserveValueImports/verbatimModuleSyntax/importsNotUsedAsValues — still use an explicit type import to avoid runtime import issues.

File: frontend/src/app/snapshots/[id]/page.tsx

-import { SnapshotDetails } from 'types/snapshot'
+import type { SnapshotDetails } from 'types/snapshot'
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)

13-13: Align local state types to generated query types to avoid drift.

Prefer types derived from GetProgramAndModulesQuery so state matches the actual selection set.

-import { Program, Module } from 'types/mentorship'
+import type { GetProgramAndModulesQuery } from 'types/__generated__/graphql'

And later:

-const [program, setProgram] = useState<Program | null>(null)
-const [modules, setModules] = useState<Module[]>([])
+type ProgramT = NonNullable<GetProgramAndModulesQuery['getProgram']>
+type ModuleT = GetProgramAndModulesQuery['getProgramModules'][number]
+const [program, setProgram] = useState<ProgramT | null>(null)
+const [modules, setModules] = useState<ModuleT[]>([])

31-33: Resolve the TODO or remove it to keep migration clean.

If you plan to rely on refetchQueries, drop the TODO; otherwise implement an update/optimisticResponse for cache sync.

I can wire an optimistic response for status flips if you want a follow‑up patch.


2-2: Prefer root Apollo hooks export ('@apollo/client') instead of '@apollo/client/react'.

Project uses @apollo/[email protected] (frontend/pnpm-lock.yaml); change the import in frontend/src/app/my/mentorship/programs/[programKey]/page.tsx from
import { useMutation, useQuery } from '@apollo/client/react'
to
import { useMutation, useQuery } from '@apollo/client'.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13c6f52 and 98858fc.

⛔ Files ignored due to path filters (14)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • frontend/src/types/__generated__/apiKeyQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/graphql.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/homeQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/moduleMutations.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/moduleQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/organizationQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/programsMutations.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/programsQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/projectQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/repositoryQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/snapshotQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/userQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (40)
  • frontend/graphql-codegen.ts (2 hunks)
  • frontend/package.json (2 hunks)
  • frontend/src/app/about/page.tsx (4 hunks)
  • frontend/src/app/chapters/[chapterKey]/page.tsx (1 hunks)
  • frontend/src/app/committees/[committeeKey]/page.tsx (3 hunks)
  • frontend/src/app/members/[memberKey]/page.tsx (3 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx (3 hunks)
  • frontend/src/app/mentorship/programs/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4 hunks)
  • frontend/src/app/my/mentorship/programs/create/page.tsx (2 hunks)
  • frontend/src/app/organizations/[organizationKey]/layout.tsx (1 hunks)
  • frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (2 hunks)
  • frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (2 hunks)
  • frontend/src/app/page.tsx (3 hunks)
  • frontend/src/app/projects/[projectKey]/layout.tsx (2 hunks)
  • frontend/src/app/projects/[projectKey]/page.tsx (2 hunks)
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (4 hunks)
  • frontend/src/app/projects/dashboard/metrics/page.tsx (6 hunks)
  • frontend/src/app/projects/dashboard/page.tsx (3 hunks)
  • frontend/src/app/settings/api-keys/page.tsx (6 hunks)
  • frontend/src/app/snapshots/[id]/layout.tsx (2 hunks)
  • frontend/src/app/snapshots/[id]/page.tsx (4 hunks)
  • frontend/src/app/snapshots/page.tsx (2 hunks)
  • frontend/src/types/chapter.ts (1 hunks)
  • frontend/src/types/committee.ts (1 hunks)
  • frontend/src/types/healthMetrics.ts (1 hunks)
  • frontend/src/types/issue.ts (1 hunks)
  • frontend/src/types/mentorship.ts (3 hunks)
  • frontend/src/types/milestone.ts (1 hunks)
  • frontend/src/types/organization.ts (1 hunks)
  • frontend/src/types/project.ts (1 hunks)
  • frontend/src/types/pullRequest.ts (1 hunks)
  • frontend/src/types/release.ts (1 hunks)
  • frontend/src/types/user.ts (1 hunks)
  • frontend/src/utils/structuredData.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/app/organizations/[organizationKey]/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/projects/dashboard/page.tsx
  • frontend/package.json
  • frontend/src/app/my/mentorship/programs/create/page.tsx
  • frontend/src/app/committees/[committeeKey]/page.tsx
  • frontend/src/app/about/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
  • frontend/src/app/projects/[projectKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
  • frontend/src/app/snapshots/page.tsx
  • frontend/src/app/projects/dashboard/metrics/page.tsx
  • frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
  • frontend/src/app/chapters/[chapterKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/page.tsx
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/mentorship/programs/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/mentorship/programs/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/mentorship/programs/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/src/app/mentorship/programs/page.tsx
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
  • frontend/src/types/healthMetrics.ts
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
  • frontend/src/types/healthMetrics.ts
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.

Applied to files:

  • frontend/src/app/snapshots/[id]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
📚 Learning: 2025-09-21T11:34:33.377Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.

Applied to files:

  • frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.

Applied to files:

  • frontend/src/app/members/[memberKey]/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
🧬 Code graph analysis (21)
frontend/src/types/pullRequest.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/types/committee.ts (1)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/app/projects/[projectKey]/layout.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (1)
  • GetProjectMetadataDocument (37-37)
frontend/src/app/mentorship/programs/page.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
  • ProgramStatusEnum (22-27)
frontend/src/types/chapter.ts (1)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/types/milestone.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/types/user.ts (4)
backend/apps/github/models/user.py (1)
  • User (19-159)
frontend/src/types/issue.ts (1)
  • Issue (3-19)
frontend/src/types/release.ts (1)
  • Release (3-13)
frontend/src/types/project.ts (1)
  • RepositoryCardProps (52-62)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
frontend/src/types/__generated__/repositoryQueries.generated.ts (1)
  • GetRepositoryMetadataDocument (22-22)
frontend/src/app/settings/api-keys/page.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (3)
  • GetApiKeysDocument (25-25)
  • CreateApiKeyDocument (26-26)
  • RevokeApiKeyDocument (27-27)
frontend/src/app/snapshots/[id]/layout.tsx (1)
frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
  • GetSnapshotDetailsMetadataDocument (25-25)
frontend/src/types/release.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)
frontend/src/types/healthMetrics.ts (1)
  • HealthMetricsProps (26-54)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (1)
  • GetProjectHealthMetricsDetailsDocument (28-28)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)
frontend/src/app/global-error.tsx (1)
  • handleAppError (66-86)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetProgramAndModulesDocument (37-37)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/types/mentorship.ts (2)
backend/apps/mentorship/api/internal/nodes/enum.py (2)
  • ProgramStatusEnum (22-27)
  • ExperienceLevelEnum (12-18)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/app/snapshots/[id]/page.tsx (3)
frontend/src/types/snapshot.ts (1)
  • SnapshotDetails (5-13)
frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
  • GetSnapshotDetailsDocument (24-24)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/types/project.ts (2)
frontend/src/types/healthMetrics.ts (1)
  • HealthMetricsProps (26-54)
frontend/src/types/contributor.ts (1)
  • Contributor (1-8)
frontend/src/types/issue.ts (1)
frontend/src/types/user.ts (2)
  • User (10-32)
  • RepositoryDetails (5-8)
frontend/src/app/members/[memberKey]/page.tsx (2)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/types/__generated__/userQueries.generated.ts (1)
  • GetUserDataDocument (27-27)
frontend/src/app/my/mentorship/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetMyProgramsDocument (35-35)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (4)
frontend/src/types/mentorship.ts (1)
  • Module (42-54)
frontend/src/types/__generated__/moduleQueries.generated.ts (1)
  • GetProgramAdminsAndModulesDocument (30-30)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/components/ModuleCard.tsx (1)
  • getSimpleDuration (98-110)
frontend/src/utils/structuredData.ts (2)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/types/profilePageStructuredData.ts (1)
  • ProfilePageStructuredData (1-31)
🔇 Additional comments (34)
frontend/src/types/chapter.ts (2)

3-3: No generated Chapter type found under generated; verify codegen outputs or rename local type

rg shows a local export type Chapter = … in frontend/src/types/chapter.ts and no matches under generated; confirm whether codegen emits a Chapter type elsewhere—if it does, re‑export the generated type; otherwise rename this local type (e.g., DomainChapter) or mark it @deprecated to avoid confusion.


5-5: createdAt/updatedAt OK as number for Chapter — no change required.

Generated types declare ChapterNode.createdAt/updatedAt as number (e.g. frontend/src/types/generated/snapshotQueries.generated.ts and frontend/src/types/generated/homeQueries.generated.ts), so keep createdAt?: number in frontend/src/types/chapter.ts unless you change the GraphQL scalar mapping.

frontend/src/types/committee.ts (1)

8-8: Require Committee.key or justify optionality

  • File: frontend/src/types/committee.ts — don't weaken the primary type. If production always provides the identifier, revert:
-  key?: string
+  key: string
  • If some payloads legitimately omit it, introduce a separate partial/preview type (e.g., CommitteePreview) instead of loosening the main Committee type.
  • Avoid spreading Committee into JSX (React reserves key); if spreads are needed, rename the field (e.g., committeeKey) in a follow‑up.
frontend/src/types/healthMetrics.ts (1)

26-54: Optionalizing most fields — confirm null vs undefined semantics and downstream usage

Findings: frontend/tsconfig.json sets "strict": false and does not set strictNullChecks or exactOptionalPropertyTypes. I could not locate GraphQL codegen config or package.json in the repo root.

  • Verify GraphQL codegen nullability (avoidOptionals / maybeValue / nonOptionalTypename) — does codegen emit null (Maybe) or omit fields (undefined)?
  • Scan consumers for arithmetic/sorting/filters and React key usage on newly-optional fields (ageDays, lastCommitDays, score, projectKey, starsCount, forksCount, openIssuesCount, totalIssuesCount) and add runtime guards or adapt types.
  • If aligning types, match codegen semantics (e.g., export type Maybe = T | null) instead of mixing null and undefined.
frontend/src/types/mentorship.ts (1)

52-53: Guard optional domains/tags at call sites or default to []

domains/tags are now optional — ensure no code does array ops on undefined. Review/adjust these call-sites (guard or default to []):

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx — tags={program.tags} (line ~86), domains={program.domains} (line ~87)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx — tags={program.tags} (149), domains={program.domains} (150)
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx — tags={module.tags} (65), domains={module.domains} (66)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx — tags={module.tags} (65), domains={module.domains} (66)
  • frontend/src/components/ModuleForm.tsx — value={formData.domains} (168), value={formData.tags} (182)
  • frontend/src/components/ProgramForm.tsx — value={formData.tags} (166), value={formData.domains} (180)
  • create/edit pages use parseCommaSeparated(formData.*) — confirm parseCommaSeparated handles undefined (create/page.tsx, modules/create/page.tsx, edit/page.tsx).
  • Already guarded (OK): frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx — (program.tags || []).join(', '), (program.domains || []).join(', ') (lines ~79–80); modules edit page has similar guards.

Fix patterns: (x ?? []).map(...), x?.length ?? 0, or default array when forwarding props (e.g., tags={program.tags ?? []}).

frontend/graphql-codegen.ts (2)

34-34: Scalar type configuration aligned with Apollo Client v4 migration.

The change from 'unknown' to 'any' for the default scalar type follows a practical approach for the Apollo Client v4 migration, where unknown scalar types need to be handled more flexibly during the transition phase.


53-60: Good scalar mapping for Date and DateTime fields.

The scalar configuration correctly maps GraphQL Date and DateTime scalars to 'string | number', which aligns with the broader migration pattern seen across the codebase where date fields are being cast to strings for formatting functions.

frontend/src/app/mentorship/programs/page.tsx (2)

5-5: Consistent enum usage adoption.

Good migration from string literals to the generated ProgramStatusEnum - this improves type safety and aligns with the broader pattern of using generated GraphQL types throughout the codebase.


55-56: Verify enum value consistency with backend

frontend/src/app/mentorship/programs/page.tsx (lines 55–56):

{programs &&
  programs.filter((p) => p.status === ProgramStatusEnum.Published).map(renderProgramCard)}

ast-grep returned no matches for ProgramStatusEnum or generated GraphQL ProgramStatusEnum in the repo. Confirm that ProgramStatusEnum.Published serializes to the backend value 'published'; if it does not, replace with the correct string or update the enum mapping.

frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)

4-4: Successfully migrated to generated GraphQL document.

The import and usage update from GET_REPOSITORY_METADATA to GetRepositoryMetadataDocument correctly follows the Apollo Client v4 migration pattern with generated document nodes.

Also applies to: 17-17

frontend/src/app/projects/[projectKey]/layout.tsx (1)

4-4: Clean migration to generated GraphQL documents.

The replacement of GET_PROJECT_METADATA with GetProjectMetadataDocument is consistent with the project-wide migration to generated GraphQL types and maintains the same functional behavior.

Also applies to: 16-16

frontend/src/types/organization.ts (1)

2-16: Type flexibility improvements for GraphQL integration.

Making most Organization properties optional (except login) aligns well with GraphQL's nullable field handling and provides better compatibility with generated types. This change supports partial data scenarios common in GraphQL queries.

frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (4)

2-2: Apollo v4 React hooks import correctly updated.

The import path change to @apollo/client/react aligns with Apollo Client v4's new entrypoint structure.


7-7: Generated GraphQL document migration completed.

The migration from GET_PROGRAM_ADMINS_AND_MODULES to GetProgramAdminsAndModulesDocument follows the established pattern throughout the codebase.

Also applies to: 20-20


15-15: Good route parameter typing improvement.

Adding explicit types to useParams<{ programKey: string; moduleKey: string }>() improves type safety.


52-57: Date handling casts are fragile.

The type casting of startedAt/endedAt to string is potentially unsafe if the backend returns epoch seconds. Based on the codegen configuration showing Date/DateTime scalars mapped to 'string | number', these fields could be numbers.

frontend/src/app/my/mentorship/page.tsx (2)

2-2: **Apollo Client v4 React hooks import is correct.**The import from @apollo/client/react is correct for Apollo Client v4. React hooks now live in the @apollo/client/react entrypoint, allowing developers to use Apollo Client with any JavaScript framework without React dependencies.


11-11: Successful migration to generated GraphQL document.

The replacement of GET_MY_PROGRAMS with GetMyProgramsDocument follows the established pattern throughout the codebase and maintains the same functionality.

Also applies to: 56-56

frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (3)

2-2: Apollo Client v4 React hooks import is correct.

The updated import path aligns with Apollo Client v4's new module structure.


17-17: GraphQL document migration completed successfully.

The replacement of GET_PROJECT_HEALTH_METRICS_DETAILS with GetProjectHealthMetricsDetailsDocument follows the established migration pattern.

Also applies to: 34-34


27-27: Good route parameter typing enhancement.

Adding explicit typing to useParams<{ projectKey: string }>() improves type safety.

frontend/src/types/milestone.ts (1)

4-12: Optionalizing many Milestone fields—verify downstream null-safety.

author, body, closedIssuesCount, createdAt, openIssuesCount, repositoryName, and state are now optional. Please double-check all consumers (rendering, sorting, formatting) for unchecked property access and add guards where needed to avoid runtime undefined issues.

frontend/src/types/user.ts (1)

14-31: createdAt/updatedAt widened to number | string and many fields optional—audit call sites.

Ensure all usages (e.g., date formatters, comparisons, arithmetic) handle both string and number, and that optional fields are null‑safe. This change can silently break UI if any code assumed required numbers.

frontend/src/types/release.ts (1)

4-5: LGTM: author/isPreRelease made optional to match varying data.

Change aligns with broader optionality adjustments and should be safe with null‑checks in consumers.

frontend/src/types/project.ts (1)

19-39: Project surface relaxed—verify consumers for null/shape assumptions.

Many fields are now optional (and organizations is a string). Please sanity‑check renderers, filters, and sorters that previously assumed presence or array/object shapes.

frontend/src/app/snapshots/[id]/page.tsx (5)

9-9: Good move to codegen documents.

Using GetSnapshotDetailsDocument aligns with Apollo v4 + codegen.


22-22: Correct useParams typing.

Explicit param typing is appropriate here.


27-29: useQuery usage matches Apollo v4 expectations.

Document + variables wiring looks correct.


124-125: Don’t force date scalars to string; pass through as-is.

formatDate already accepts number | string. The casts are unnecessary and can hide type issues.

Apply:

-                  {formatDate(snapshot.startAt as string)} - {formatDate(snapshot.endAt as string)}
+                  {formatDate(snapshot.startAt)} - {formatDate(snapshot.endAt)}

2-2: Approve: Apollo hook imports are consistent in frontend/src.
Search shows React hook imports use '@apollo/client/react' across frontend/src; remaining '@apollo/client' usages are server-side (ApolloClient/gql) and expected.

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)

10-11: LGTM: generated enums and documents are correctly adopted.

Using ProgramStatusEnum and the typed GetProgramAndModulesDocument aligns with Apollo v4 + codegen best practices.


36-44: LGTM: useQuery with typed document + skip flag.

Typed variables and notifyOnNetworkStatusChange fit the migration; skip guards undefined params.


79-79: Apollo v4 note: refetchQueries are not awaited; verify UX expectations.

If the toast/navigation should occur after data is refreshed, explicitly await refetch() or manually await the returned refs; otherwise current behavior is fine.

Minimal alternative:

-      await updateProgram({
+      await updateProgram({
         variables: { /* ... */ },
-        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
+        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
       })
+      await refetch()

130-133: Harden date formatting and humanize enum; avoid unsafe casts.

  • Casting to string can yield "Invalid date" for numeric epochs and can still throw in formatDate. Pass number|string with null guards.
  • ProgramStatusEnum values are typically UPPER_CASE; upperFirst keeps them shouting. Lowercase then title‑case for display.
-  const programDetails = [
-    { label: 'Status', value: upperFirst(program.status) },
-    { label: 'Start Date', value: formatDate(program.startedAt as string) },
-    { label: 'End Date', value: formatDate(program.endedAt as string) },
+  const programDetails = [
+    { label: 'Status', value: upperFirst(program.status.toLowerCase()) },
+    { label: 'Start Date', value: program.startedAt ? formatDate(program.startedAt as number | string) : 'N/A' },
+    { label: 'End Date', value: program.endedAt ? formatDate(program.endedAt as number | string) : 'N/A' },

Additionally (elsewhere in this file), for the toast:

- title: `Program status updated to ${upperFirst(newStatus)}`,
+ title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`,

Optional follow‑up: map Date/DateTime scalars to string in codegen.yml to avoid unknown and casts.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
frontend/src/app/snapshots/[id]/page.tsx (3)

2-2: Confirm import path choice for Apollo v4 hooks.

Both '@apollo/client' and '@apollo/client/react' work in v4. The docs generally show root imports; mixing entrypoints across files can increase bundle duplication. If consistency is a goal, consider the root import.

- import { useQuery } from '@apollo/client/react'
+ import { useQuery } from '@apollo/client'

12-12: Make SnapshotDetails a type-only import.

Avoids creating a runtime import for a type-only symbol.

- import { SnapshotDetails } from 'types/snapshot'
+ import type { SnapshotDetails } from 'types/snapshot'

145-146: Remove stray whitespace text node in JSX.

The {' '} fragment adds an unnecessary text child and may trip linters.

-            {' '}
             {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}
frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)

8-8: Use type‑only import to avoid bundling the types module

Prevents accidental runtime import and improves tree‑shaking.

-import { Program, Module } from 'types/mentorship'
+import type { Program, Module } from 'types/mentorship'

14-14: Prefer typed useParams over assertion (match modules page)

Consistent, safer typing without a cast.

-const { programKey } = useParams() as { programKey: string }
+const { programKey } = useParams<{ programKey: string }>()
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)

8-8: Type‑only import for Module

Avoids emitting a runtime import.

-import { Module } from 'types/mentorship'
+import type { Module } from 'types/mentorship'

17-17: Optional: type admins state

If DetailsCard expects a Contributor[] (likely), type it to preserve inference.

-const [admins, setAdmins] = useState(null)
+import type { Contributor } from 'types/mentorship'
+const [admins, setAdmins] = useState<Contributor[] | null>(null)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98858fc and 2b52229.

📒 Files selected for processing (7)
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (2 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1 hunks)
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (3 hunks)
  • frontend/src/app/settings/api-keys/page.tsx (5 hunks)
  • frontend/src/app/snapshots/[id]/page.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/app/settings/api-keys/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.

Applied to files:

  • frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-09-21T11:34:33.377Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.

Applied to files:

  • frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
🧬 Code graph analysis (4)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/snapshot.ts (1)
  • SnapshotDetails (5-13)
frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
  • GetSnapshotDetailsDocument (24-24)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)
frontend/src/types/mentorship.ts (1)
  • Module (42-54)
frontend/src/types/__generated__/moduleQueries.generated.ts (1)
  • GetProgramAdminsAndModulesDocument (30-30)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetProgramAndModulesDocument (37-37)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)
frontend/src/types/healthMetrics.ts (1)
  • HealthMetricsProps (26-54)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (1)
  • GetProjectHealthMetricsDetailsDocument (28-28)
🔇 Additional comments (12)
frontend/src/app/snapshots/[id]/page.tsx (3)

9-9: Good: switched to generated Document for typed queries.

Using GetSnapshotDetailsDocument aligns with Apollo v4 + codegen best practices.


22-22: Typed route params look correct.

useParams<{ id: string }>() is appropriate for /snapshots/[id].


27-29: LGTM on useQuery with generated document.

Inference from the typed DocumentNode obviates generics; variables usage is correct.

frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)

2-2: Apollo v4 hook import — LGTM

Correctly importing React hooks from @apollo/client/react.


7-7: Switch to generated document — LGTM; Date/DateTime scalars emitted as any in generated types (fix required)

graphql-codegen.ts maps Date/DateTime -> 'string | number' for ./src/types/generated/graphql.ts, but operation-generated files still emit startedAt/endedAt as any.
Affected: frontend/src/types/generated/programsQueries.generated.ts, programsMutations.generated.ts, moduleQueries.generated.ts, moduleMutations.generated.ts.
Action: apply the scalars mapping to the operation outputs (or use a global mapping), re-run codegen, and verify startedAt/endedAt are typed 'string | number' (not any).

frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)

2-2: Apollo v4 hook import — LGTM


7-7: Generated document + useQuery wiring — LGTM

Document and variables match the query signature.

Also applies to: 20-25


15-15: Typed useParams — LGTM

Good switch to generic typing for route params.

frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (4)

27-27: Typed useParams for App Router is spot on.

Matches prior learning about extracting params in Next 13+. Thanks for keeping this scoped to migration.


34-36: useQuery with the generated document and inline variables — LGTM.

Types are inferred from the DocumentNode; no generics needed.


17-17: Approve — generated DocumentNode usage is correct; legacy constant only defined, not referenced elsewhere

GetProjectHealthMetricsDetailsDocument is imported/used in frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx; the legacy GET_PROJECT_HEALTH_METRICS_DETAILS only exists at frontend/src/server/queries/projectsHealthDashboardQueries.ts and has no other references — safe to remove the legacy constant if you want to clean up.


2-2: Switch to @apollo/client/react — app code migrated; tests need updates

App pages/hooks/components now import from @apollo/client/react (v4 migration looks correct). Server-side modules (frontend/src/server/** and utils/helpers/apolloClient.ts) correctly keep top-level @apollo/client for non‑React exports (ApolloClient, gql, link/context).
Many unit tests still import hooks from the top-level @apollo/client (e.g. frontend/tests/unit/pages/About.test.tsx, ProjectDetails.test.tsx, CreateModule.test.tsx, EditModule.test.tsx, ApiKeysPage.test.tsx and other files under frontend/tests/unit/pages/**) — update those tests or their jest.mock targets to import/mock @apollo/client/react so hooks resolve to the React entrypoint.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
frontend/__tests__/unit/pages/About.test.tsx (2)

532-541: Do not compare to legacy GET_ if the component uses generated documents.*

Align this condition with operation-name matching or generated documents.


116-133: Fix fragile useQuery mock in About.test.tsx — match generated documents or operation name

frontend/src/app/about/page.tsx uses generated documents (GetProjectMetadataDocument, GetTopContributorsDocument, GetLeaderDataDocument); the test's mock in frontend/tests/unit/pages/About.test.tsx (lines 116–133) currently compares against GET_* and will break. Update the mock to either match the operation name (document.definitions[0].name.value) or import and compare against the same generated Document objects used by the component.

frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)

67-76: Replace legacy mutation identity checks with generated DocumentNodes

frontend/src/app/settings/api-keys/page.tsx imports CreateApiKeyDocument and RevokeApiKeyDocument; the test currently compares against CREATE_API_KEY/REVOKE_API_KEY and will break. Update the mock in frontend/tests/unit/pages/ApiKeysPage.test.tsx (lines 67–76) to compare against CreateApiKeyDocument / RevokeApiKeyDocument (or match by operation name) instead of the old constants.

🧹 Nitpick comments (31)
frontend/__tests__/unit/utils/structuredData.test.ts (3)

1-1: Use type‑only import for clarity and to avoid accidental value import.
Switch to import type since User is a type alias.

-import { User } from 'types/user'
+import type { User } from 'types/user'

10-19: Fix mockUser date fields to match expected normalized ISO timestamps.
Your expectations use epoch+2020s and +2021s (1970-01-01T00:33:40Z / 00:33:41Z). Update the fixture to numbers to avoid mismatch.

-  createdAt: '2020-01-01T00:00:00Z',
+  createdAt: 2020,
   email: '[email protected]',
@@
-  updatedAt: '2021-02-03T00:00:00Z',
+  updatedAt: 2021,

If formatISODate treats numbers as seconds (not ms), this will pass. If it expects ms, use 2000 and 2001 seconds × 1000 respectively. Please run the unit tests to confirm.


61-72: Fix minimalUser date fields to match expected normalization.
Mirror the same numeric dates so the second test remains consistent.

-  createdAt: '2020-01-01T00:00:00Z',
+  createdAt: 2020,
@@
-  updatedAt: '2021-02-03T00:00:00Z',
+  updatedAt: 2021,
frontend/__tests__/unit/pages/MyMentorship.test.tsx (1)

33-33: Casting via unknown works; optionally prefer jest.MockedFunction typing.
Keeps types tighter without double cast.

Apply if desired:

-const mockUseQuery = useQuery as unknown as jest.Mock
+const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>

If this trips on generics/overloads in your tsconfig, keep the current cast to stay unblocked for migration.

frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1)

64-69: Optional: DRY the useQuery mock cast

Avoid repeating (useQuery as unknown as jest.Mock) by defining a single typed alias and reusing it.

Add once near the imports:

const mockedUseQuery = useQuery as unknown as jest.Mock

Then update the call sites:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    mockedUseQuery.mockReturnValue({

(Apply the same replacement at Lines 75, 88, and 177.)

Also applies to: 75-80, 88-93, 177-182

frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2)

46-46: Remove repetitive double-casts by aliasing the mock once.

Define a typed alias once and use it, improving readability and avoiding repeated casts.

Apply this diff for the repeated usage lines:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    useQueryMock.mockReturnValue({

Add this alias near the top (after imports):

const useQueryMock = useQuery as unknown as jest.Mock

Also applies to: 58-58, 72-72, 89-89, 156-156, 171-171, 197-197, 218-218, 264-264, 280-280


58-61: Set loading explicitly for the loading-state test.

To avoid relying on component internals, return loading: true when asserting the spinner.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
-      data: null,
-      error: null,
-    })
+    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+      data: null,
+      loading: true,
+      error: null,
+    })
frontend/__tests__/unit/pages/EditModule.test.tsx (1)

26-29: Preserve all exports when mocking @apollo/client.

Spread the actual module to avoid unintentionally nulling other exports used transitively (e.g., gql, types, utility fns).

-jest.mock('@apollo/client', () => ({
-  useApolloClient: jest.fn(),
-  gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => ({
+  ...jest.requireActual('@apollo/client'),
+  useApolloClient: jest.fn(),
+}))
frontend/__tests__/unit/pages/ModuleDetails.test.tsx (1)

34-34: Casting approach is fine; optional stronger typing.

Current cast is acceptable. Optionally, tighten typing if TS allows:

-  const mockUseQuery = useQuery as unknown as jest.Mock
+  const mockUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>
frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (4)

8-11: Mocking the React entrypoint looks good; reduce repeated casts.

To avoid repeating (useQuery as unknown as jest.Mock) everywhere, create a single alias:

Add near the imports:

const mockUseQuery = useQuery as unknown as jest.Mock

Then replace usages with mockUseQuery.mockReturnValue(...).


51-56: Fix brittle loading assertion (getAllByAltText evaluated too early).

getAllByAltText is called before the waitFor, so the test can fail immediately if the spinner renders asynchronously. Use findAllByAltText or move the query inside waitFor.

Replace the assertion block with one of:

// Option A (preferred)
const loadingSpinner = await screen.findAllByAltText('Loading indicator')
expect(loadingSpinner.length).toBeGreaterThan(0)

// Option B
await waitFor(() => {
  expect(screen.getAllByAltText('Loading indicator').length).toBeGreaterThan(0)
})

66-69: Be explicit about loading=false to avoid flakiness.

Not required, but adding loading: false makes intent clear if the component branches on loading.


83-86: Use an Error/ApolloError instance for the error shape.

Right now error is { error: Error }. If the component checks error.message or expects an ApolloError, this may misbehave.

Consider:

import { ApolloError } from '@apollo/client'
// ...
const mockError = new ApolloError({ errorMessage: 'GraphQL error' })
// ...
(useQuery as unknown as jest.Mock).mockReturnValue({
  data: null,
  error: mockError,
})

Or, minimally:

const mockError = new Error('GraphQL error')
// ...
error: mockError,
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx (1)

23-23: DRY the repeated casts by defining a single typed mock for useQuery.

Keeps casts in one place and improves readability.

Apply this change here:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    mockedUseQuery.mockReturnValue({
       data: mockProgramDetailsData,
       loading: false,
       refetch: jest.fn(),
     })

Add this once near the imports (outside describe):

// after imports
const mockedUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>
frontend/__tests__/unit/pages/CreateProgram.test.tsx (1)

37-37: Prefer typed mock helper over double-cast

Use jest.mocked for cleaner typing and readability.

-    ;(useMutation as unknown as jest.Mock).mockReturnValue([mockCreateProgram, { loading: false }])
+    jest.mocked(useMutation).mockReturnValue([mockCreateProgram, { loading: false }])
frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx (1)

34-34: Prefer precise typing over unknown as jest.Mock.

Use jest.MockedFunction for safer typings.

Apply:

-  const mockUseQuery = useQuery as unknown as jest.Mock
+  const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>
frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (1)

39-49: Fix potential flakiness in loading-spinner assertion.

Capturing elements before waiting can throw; use findAllBy* or assert inside waitFor.

Apply:

-    const loadingSpinner = screen.getAllByAltText('Loading indicator')
-    await waitFor(() => {
-      expect(loadingSpinner.length).toBeGreaterThan(0)
-    })
+    const loadingSpinner = await screen.findAllByAltText('Loading indicator')
+    expect(loadingSpinner.length).toBeGreaterThan(0)
frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (1)

40-44: Avoid repeating casts; create a local mock alias for useQuery

Define a typed alias once and use it across tests to reduce noise.

Apply this diff to the current line:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    ;mockUseQuery.mockReturnValue({

Add this helper near the top (e.g., after the Apollo mock):

// place once near the mocks
const mockUseQuery = useQuery as unknown as jest.Mock;
frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (4)

35-37: Mock error shape should be an Error (or ApolloError), not { error: Error }

Use a direct Error to better match useQuery’s error type and typical component checks.

Apply:

-const mockError = {
-  error: new Error('GraphQL error'),
-}
+const mockError = new Error('GraphQL error')

41-46: Casting works; optional: tighten typing with jest.MockedFunction

This avoids double-casting to unknown and preserves useQuery’s call signature in mocks.

Apply:

-;(useQuery as unknown as jest.Mock).mockReturnValue({
+;(useQuery as unknown as jest.MockedFunction<typeof useQuery>).mockReturnValue({
   data: mockProjectsDashboardOverviewData,
   loading: false,
   error: null,
 })

52-56: Same optional typing improvement for the loading state mock

Keeps mocks consistently typed.

Apply:

-;(useQuery as unknown as jest.Mock).mockReturnValue({
+;(useQuery as unknown as jest.MockedFunction<typeof useQuery>).mockReturnValue({
   data: null,
   loading: true,
   error: null,
 })

65-69: Error state mock: align with direct Error instance

After adjusting mockError to Error, this will better reflect runtime behavior and component expectations.

Optionally, use .mockReturnValueOnce(...) here to avoid cross-test bleed and rely on the default set in beforeEach.

frontend/__tests__/unit/pages/UserDetails.test.tsx (1)

47-47: Explicitly set loading in all useQuery mocks; fix the loading-state test; optional typed helper to reduce casts.

  • Some overrides omit loading. Be explicit to avoid fragile tests if component logic changes.
  • The “renders loading state” test should mock loading: true to reflect intent.

Apply this diff for the loading test:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
-      data: null,
-      error: null,
-    })
+    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+      data: null,
+      error: null,
+      loading: true,
+    })

Apply this diff for the error test:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
-      data: null,
-      error: mockError,
-    })
+    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+      data: null,
+      error: mockError,
+      loading: false,
+    })

Optional (to reduce repeated casts):

// near the top of the file
const mockedUseQuery = useQuery as unknown as jest.Mock;

// usage
mockedUseQuery.mockReturnValue({ data: ..., loading: false, error: null })

Also applies to: 63-66, 76-83, 102-106, 123-127, 141-145, 159-163, 181-185, 205-209, 232-236, 255-258, 270-273, 339-343, 357-361, 376-380, 395-398, 412-416, 430-433, 452-456, 477-481, 492-495

frontend/__tests__/unit/pages/CreateModule.test.tsx (2)

59-66: Prefer precise mock typing over unknown casts.

Use jest.MockedFunction to avoid unsafe unknown as jest.Mock casts.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    ;(useQuery as jest.MockedFunction<typeof useQuery>).mockReturnValue({
       data: {
         getProgram: {
           admins: [{ login: 'admin-user' }],
         },
       },
       loading: false,
     })

67-70: Type the mutation mock tuple precisely.

This keeps the mock shape aligned with Apollo’s useMutation return type.

-    ;(useMutation as unknown as jest.Mock).mockReturnValue([
+    ;(useMutation as jest.MockedFunction<typeof useMutation>).mockReturnValue([
       mockCreateModule.mockResolvedValue({}),
       { loading: false },
     ])
frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (2)

49-60: Set loading: true and avoid pre-querying before waitFor in the loading test.
Currently the mock omits loading, and getAllByAltText is called before the UI can update.

Apply this diff to set loading explicitly:

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
-      data: null,
-      error: null,
-    })
+    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+      data: null,
+      loading: true,
+      error: null,
+    })

Optionally, prefer an async query to reduce flakiness:

// Replace the getAllByAltText + waitFor with:
const loadingSpinner = await screen.findAllByAltText('Loading indicator')
expect(loadingSpinner.length).toBeGreaterThan(0)

82-86: Use an Error (or ApolloError) directly for the error field.
Passing { error: Error } nests the error and may not match expectations.

Apply this diff to pass the actual error:

-      error: mockError,
+      error: mockError.error,

Alternatively, construct an ApolloError if you want closer parity with v4:

import { ApolloError } from '@apollo/client'
// ...
error: new ApolloError({ networkError: new Error('GraphQL error') }),
frontend/__tests__/unit/pages/EditProgram.test.tsx (1)

38-38: Prefer typed mocking to avoid unknown as jest.Mock casts

Use jest.mocked(useQuery) or MockedFunction<typeof useQuery> for type-safe returns.

Example:

import type { MockedFunction } from 'jest-mock'
const mockUseQuery = useQuery as unknown as MockedFunction<typeof useQuery>
mockUseQuery.mockReturnValue({ loading: true } as any)
frontend/__tests__/unit/pages/ProgramDetails.test.tsx (1)

23-27: Avoid repeated casts by extracting a typed alias once.

Define a single const and reuse to reduce repetition.

Apply this diff here and reuse the alias throughout the file:

 describe('ProgramDetailsPage', () => {
+  const mockUseQuery = useQuery as unknown as jest.Mock
   beforeEach(() => {
-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    mockUseQuery.mockReturnValue({
       data: mockProgramDetailsData,
       loading: false,
       refetch: jest.fn(),
     })
   })

Change other occurrences similarly (see Lines 35 and 48).

frontend/__tests__/unit/pages/Home.test.tsx (1)

73-78: Extract a single mock alias to avoid repeated casts.

Define mockUseQuery once and reuse.

Apply:

 describe('Home', () => {
   let mockRouter: { push: jest.Mock }
 
   beforeEach(() => {
-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    const mockUseQuery = useQuery as unknown as jest.Mock
+    mockUseQuery.mockReturnValue({
       data: mockGraphQLData,
       loading: false,
       error: null,
     })

Replicate for other occurrences below (Lines 88, 113, 196, 248).

frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)

51-52: Typed aliases for mocks improve clarity.

Good pattern; consider jest.MockedFunction<typeof useQuery/useMutation> for tighter typing (optional).

-const mockUseQuery = useQuery as unknown as jest.Mock
-const mockUseMutation = useMutation as unknown as jest.Mock
+const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>
+const mockUseMutation = useMutation as jest.MockedFunction<typeof useMutation>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b52229 and 32da88b.

📒 Files selected for processing (27)
  • frontend/__tests__/unit/components/ProgramCard.test.tsx (3 hunks)
  • frontend/__tests__/unit/components/SingleModuleCard.test.tsx (2 hunks)
  • frontend/__tests__/unit/data/mockProgramData.ts (2 hunks)
  • frontend/__tests__/unit/pages/About.test.tsx (14 hunks)
  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/ChapterDetails.test.tsx (7 hunks)
  • frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (7 hunks)
  • frontend/__tests__/unit/pages/CreateModule.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/CreateProgram.test.tsx (2 hunks)
  • frontend/__tests__/unit/pages/EditModule.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/EditProgram.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/Home.test.tsx (7 hunks)
  • frontend/__tests__/unit/pages/ModuleDetails.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/MyMentorship.test.tsx (2 hunks)
  • frontend/__tests__/unit/pages/OrganizationDetails.test.tsx (9 hunks)
  • frontend/__tests__/unit/pages/ProgramDetails.test.tsx (4 hunks)
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx (4 hunks)
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx (11 hunks)
  • frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (4 hunks)
  • frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (7 hunks)
  • frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (9 hunks)
  • frontend/__tests__/unit/pages/Snapshots.test.tsx (6 hunks)
  • frontend/__tests__/unit/pages/UserDetails.test.tsx (25 hunks)
  • frontend/__tests__/unit/utils/structuredData.test.ts (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • frontend/tests/unit/pages/ChapterDetails.test.tsx
  • frontend/tests/unit/pages/Snapshots.test.tsx
  • frontend/tests/unit/pages/OrganizationDetails.test.tsx
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx
  • frontend/__tests__/unit/pages/EditProgram.test.tsx
  • frontend/__tests__/unit/pages/CreateModule.test.tsx
  • frontend/__tests__/unit/pages/ModuleDetails.test.tsx
  • frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/__tests__/unit/pages/ProgramDetails.test.tsx
  • frontend/__tests__/unit/pages/EditProgram.test.tsx
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/__tests__/unit/pages/ProgramDetails.test.tsx
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/__tests__/unit/pages/ProgramDetails.test.tsx
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/pages/ProgramDetails.test.tsx
  • frontend/__tests__/unit/pages/MyMentorship.test.tsx
  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
  • frontend/__tests__/unit/pages/ProjectDetails.test.tsx
  • frontend/__tests__/unit/pages/CreateProgram.test.tsx
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
  • frontend/__tests__/unit/pages/CommitteeDetails.test.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.

Applied to files:

  • frontend/__tests__/unit/components/SingleModuleCard.test.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/__tests__/unit/pages/EditProgram.test.tsx
  • frontend/__tests__/unit/pages/CreateModule.test.tsx
  • frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.

Applied to files:

  • frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/components/SingleModuleCard.test.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (2)
  • ProgramStatusEnum (22-27)
  • ExperienceLevelEnum (12-18)
frontend/__tests__/unit/data/mockProgramData.ts (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
  • ProgramStatusEnum (22-27)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/__tests__/unit/components/ProgramCard.test.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
  • ProgramStatusEnum (22-27)
🔇 Additional comments (80)
frontend/__tests__/unit/utils/structuredData.test.ts (2)

5-5: LGTM: test fixture typed with User.
Type aligns with types/user.ts.


26-31: Confirm units for date normalization (seconds vs milliseconds).
The expected values imply seconds-based normalization. If the helper uses ms, adjust inputs (or expectations) accordingly.

frontend/__tests__/unit/pages/MyMentorship.test.tsx (2)

1-1: Switching hook import to '@apollo/client/react' is correct for Apollo v4.
Matches the new recommended subpath for React hooks.


8-14: Ensure the component imports hooks from the same @apollo/client subpath you're mocking
You mock '@apollo/client/react' — that will not stub imports from '@apollo/client'. Either mock '@apollo/client' instead or change the component to import from '@apollo/client/react'. Automated search didn't locate the MyMentorship page in this repo; confirm the component's import path.

frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (2)

1-1: LGTM: switched hooks to @apollo/client/react

Correct entrypoint for Apollo v4 React hooks.

Please confirm the page under test also imports hooks from '@apollo/client/react' so this mock reliably intercepts calls.


6-9: Mocking strategy looks correct

Spreading the real module and overriding useQuery with a jest.fn is appropriate for these tests.

frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2)

1-1: Apollo v4 hooks entrypoint looks correct.

Good switch to '@apollo/client/react'. Please ensure the component under test imports hooks from the same entrypoint; otherwise the mock won’t intercept.


8-11: Mock target updated to the react entrypoint — LGTM.

Spreading the actual exports and overriding useQuery keeps behavior consistent.

frontend/__tests__/unit/pages/EditModule.test.tsx (4)

20-24: React-surface mocks look correct.

Spreading actual and overriding useQuery/useMutation is appropriate for this test.


63-82: Query mock shape aligns with expected component usage.

Admins and module fields look consistent for rendering and form population.


83-86: Mutation mock wiring is fine.

Returning [fn, { loading: false }] matches Apollo hook tuple.


127-127: Loading-state mock is fine.

This will exercise the spinner path as intended.

frontend/__tests__/unit/pages/ModuleDetails.test.tsx (2)

13-16: Mocking the React entrypoint is aligned with the new import path.

Good partial mock retaining real exports and overriding only useQuery.


1-1: Correct Apollo v4 hook import path.

LGTM — verified no React hooks are imported from the top-level '@apollo/client'; hooks are imported from '@apollo/client/react' in tests and app code, so the mock will intercept correctly.

frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (6)

39-44: Default mock shape LGTM.

Covers data/loading/error for the happy path.


103-106: LGTM.

Happy path mock for navigation test is fine.


122-125: LGTM.

Chapter navigation test wiring is consistent.


141-144: LGTM.

Release rendering test wiring matches the new import surface.


157-167: LGTM.

Good negative-case coverage by zeroing the new* arrays.


1-1: Correct v4 import path — approved.

Client hooks are imported from '@apollo/client/react' across the repo; server-side imports from '@apollo/client' (ApolloClient/gql/links) are intentional. No stray hook imports found.

frontend/__tests__/unit/components/SingleModuleCard.test.tsx (1)

7-7: Confirmed: generated enums export ExperienceLevelEnum and ProgramStatusEnum

frontend/src/types/generated/graphql.ts exports ExperienceLevelEnum and ProgramStatusEnum — the imports in the tests are correct.

frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx (4)

8-12: Mock target updated to @apollo/client/react — aligned with the migration.

Good move to mock the React entrypoint so it matches the new hook imports.


35-38: Same refactor applies: reuse the typed mock helper.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    mockedUseQuery.mockReturnValue({
       loading: true,
       data: null,
     })

48-51: Same refactor applies here as well.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    mockedUseQuery.mockReturnValue({
       loading: false,
       data: { program: null },
     })

1-1: Confirmed — mock will apply (both test and page use '@apollo/client/react').

frontend/tests/unit/pages/ProgramDetailsMentorship.test.tsx mocks useQuery from '@apollo/client/react' and frontend/src/app/my/mentorship/programs/[programKey]/page.tsx imports useMutation/useQuery from '@apollo/client/react'; no hook imports from '@apollo/client' found in mentorship pages.

frontend/__tests__/unit/pages/CreateProgram.test.tsx (1)

1-1: Correct Apollo v4 React entrypoint for hooks

Using '@apollo/client/react' aligns with Apollo Client v4’s React entrypoint and the PR’s migration approach.

frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx (1)

13-16: Mock wiring looks correct.

Mock path matches the import; spreading requireActual preserves other exports.

frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (7)

8-11: Mocking the React entry and overriding useQuery looks good.

Preserves actual exports while replacing useQuery with a jest.fn.


29-33: Sane default mock shape for useQuery.

Provides data and error defaults expected by the component.


64-67: Good: error-path mock for “not found”.

Matches the component’s expected error handling.


94-97: Good: covers incomplete contributor names.

Validates graceful fallback to login.


114-118: Good: GraphQL error case exercised.

Asserts user-facing message on request failure.


127-130: Good: sponsor block expectation remains explicit.

Keeps behavior pinned during migration.


1-1: Correct import path for Apollo v4 hooks.

Hook imports use '@apollo/client/react' now — verified: no remaining useQuery/useMutation/useApolloClient/etc. imports from '@apollo/client'.

frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (3)

14-16: Module mock updated to @apollo/client/react looks good

Preserving actual exports and overriding useQuery is correct for Apollo v4.


52-56: Same DRY suggestion as above for repeated casts

Use the mockUseQuery alias here as well.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    ;mockUseQuery.mockReturnValue({

65-69: Same DRY suggestion as above for repeated casts

Use the mockUseQuery alias here too.

-    ;(useQuery as unknown as jest.Mock).mockReturnValue({
+    ;mockUseQuery.mockReturnValue({
frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (1)

1-1: Correct: import React hooks from @apollo/client/react in v4

This aligns with Apollo Client v4 module structure.

frontend/__tests__/unit/pages/UserDetails.test.tsx (2)

11-12: LGTM: Mocking the React entrypoint.

Mock path matches the new hook import path.


1-1: LGTM — Apollo v4 hook imports are correct.

Searched repo (excluding tests) for useQuery/useLazyQuery/useMutation/useSubscription imported from '@apollo/client' — no matches found; hooks use '@apollo/client/react'.

frontend/__tests__/unit/pages/CreateModule.test.tsx (2)

1-2: Correct v4 hook import path.

Importing React hooks from @apollo/client/react matches Apollo v4 module boundaries.


19-25: Mocks aligned with v4 entrypoint.

Stubbing useQuery/useMutation/useApolloClient from @apollo/client/react is the right approach for Apollo v4.

frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (6)

37-41: Casting useQuery to jest.Mock is fine here.
Keeps TS quiet during migration.


63-67: Data-available mock looks fine.


149-152: No-data case mock is reasonable.


198-211: Zero-stats mock reads well for the “missing stats” scenario.


1-1: Align imports to @apollo/client/react so the test mock applies

frontend/tests/unit/pages/RepositoryDetails.test.tsx imports useQuery from '@apollo/client/react'; if the component still imports from '@apollo/client' the mock won’t intercept — verify and update all useQuery/useLazyQuery imports to '@apollo/client/react' across the repo.


8-11: Mock target aligns with import path — sandbox couldn't confirm absence of '@apollo/client' hook imports.
Run locally to list any remaining root-package hook imports and paste the output:
rg -n --hidden -S "import\s+{[^}]\buse(?:Query|LazyQuery)\b[^}]}\s+from\s+['"]@apollo/client(?:/react)?['"]" -g '!node_modules' -g '!dist' -C2
If hits are found, standardize imports to '@apollo/client/react' or add a jest.mock for '@apollo/client' as well.

frontend/__tests__/unit/pages/EditProgram.test.tsx (3)

14-21: Module mock switched to '@apollo/client/react' — LGTM

The requireActual + override pattern is correct and aligns with the new import surface.


50-57: Mock is correct — component expects data.getProgram
The component references data?.getProgram and destructures const { getProgram: program } = data in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx, so the test mock using data.getProgram is correct.


71-86: Dates: mocks are correct — no change required.
formatDateForInput(new Date(dateStr) -> toISOString().slice(0,10)) accepts '2025-01-01' and '2025-12-31' and will produce the expected 'YYYY-MM-DD' values for the date inputs.

frontend/__tests__/unit/pages/ProgramDetails.test.tsx (4)

1-1: Correct Apollo v4 hook import path.

Good switch to @apollo/client/react for hooks.


8-12: Mocking the React entrypoint is correct.

Targeting @apollo/client/react keeps mocks aligned with Apollo v4.


35-38: Same aliasing suggestion as above.

Use the mockUseQuery alias instead of re‑casting.


48-51: Same aliasing suggestion as above.

Use the mockUseQuery alias instead of re‑casting.

frontend/__tests__/unit/pages/Home.test.tsx (6)

11-14: Mocks aligned with Apollo v4 module path.

Looks good.


88-92: Same aliasing suggestion as above.

Use a mockUseQuery alias instead of re‑casting.


113-116: Same aliasing suggestion as above.

Use a mockUseQuery alias instead of re‑casting.


196-201: Same aliasing suggestion as above.

Use a mockUseQuery alias instead of re‑casting.


248-255: Same aliasing suggestion as above.

Use a mockUseQuery alias instead of re‑casting.


1-1: Approved — hooks correctly imported from '@apollo/client/react'

Repo-wide scan found no direct imports of useQuery/useMutation/etc. from '@apollo/client'. Server-side imports of gql/ApolloClient remain from '@apollo/client' (expected).

frontend/__tests__/unit/pages/About.test.tsx (12)

1-1: Correct Apollo v4 hook import path.

Good update to @apollo/client/react.


12-15: Mocks target the right module.

Mocking @apollo/client/react is correct for Apollo v4.


175-185: Heads-up: ensure this mock matches the component’s document.

If the page uses generated documents, prefer operation-name matching or comparing to the generated document as suggested above.


308-320: Same note on document identity.

Apply the operation-name or generated-document approach here as well.


339-351: LGTM.

This branch keys off variables only and doesn’t rely on document identity.


371-377: LGTM.

This mock variation is fine and doesn’t depend on query identity.


393-401: LGTM.

No identity checks; safe.


442-451: LGTM.

No identity checks; safe.


465-476: LGTM.

No identity checks; safe.


488-497: LGTM.

No identity checks; safe.


510-519: LGTM.

No identity checks; safe.


558-567: Same issue as above for topContributors.

Use operation-name or generated-document comparison.

frontend/__tests__/unit/data/mockProgramData.ts (2)

1-1: Switch to generated enum source looks correct.

Using types/generated/graphql aligns with codegen outputs.


20-20: Enum member casing updated to match codegen.

ProgramStatusEnum.Draft aligns with typical codegen (value 'DRAFT'); UI can still render “Draft”.

Confirm other tests referencing ProgramStatusEnum have updated member casing to avoid type errors.

frontend/__tests__/unit/components/ProgramCard.test.tsx (3)

5-5: Importing ProgramStatusEnum from generated types is consistent.

Good alignment with codegen.

Ensure the Program type (types/mentorship) is compatible with the generated enum type to avoid TS widening or mismatch.


36-36: Updated enum member casing is correct.

ProgramStatusEnum.Published matches codegen style.


299-299: Updated enum member casing is correct.

ProgramStatusEnum.Draft matches codegen style.

frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (2)

2-2: Correct Apollo v4 hook import path.

useQuery/useMutation from @apollo/client/react is the right entrypoint.


11-15: Mocks aligned to @apollo/client/react.

Looks good.

Comment on lines +26 to +28
jest.mock('@apollo/client', () => ({
gql: jest.requireActual('@apollo/client').gql,
}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Don’t stub @apollo/client down to only gql — pass through actual exports to avoid breakage.

Limiting the @apollo/client mock to just gql can break wrappers/providers that import ApolloClient, InMemoryCache, ApolloProvider, HttpLink, etc. Either remove this mock or pass through the full actual module.

Apply one of these:

Option A — pass through actual module:

-jest.mock('@apollo/client', () => ({
-  gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => {
+  const actual = jest.requireActual('@apollo/client')
+  return { ...actual }
+})

Option B — remove the mock entirely (preferred if not strictly needed in this test):

-jest.mock('@apollo/client', () => ({
-  gql: jest.requireActual('@apollo/client').gql,
-}))

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking if @apollo/client is used in test utilities or the page under test (would break if only gql is exported)…"

echo
echo "— Potential test utils matching 'testUtil' —"
fd -a -t f -p 'testUtil' | sed -n '1,200p' || true

echo
echo "— Imports from @apollo/client inside any test util files —"
fd -a -t f -p 'testUtil' | xargs -r rg -n -C2 -e "from ['\"]@apollo/client['\"]" -e "require\\(['\"]@apollo/client['\"]\\)" || true

echo
echo "— Imports from @apollo/client across app/ and wrappers/ —"
rg -n -C2 --type=ts --type=tsx -g 'app/**' -g 'wrappers/**' -e "from ['\"]@apollo/client['\"]" -e "require\\(['\"]@apollo/client['\"]\\)" || true

Length of output: 361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate CreateModule.test.tsx"
fd -a -t f -I CreateModule.test.tsx || true

echo
echo "Show file header around the mock in CreateModule.test.tsx (if present)"
rg -n -C3 "jest\.mock\(['\"]@apollo/client['\"]" || true
# If match found, print the file with context
MATCH_FILE=$(rg -n --no-heading "jest\.mock\(['\"]@apollo/client['\"]" -S --hidden --glob '!node_modules/**' --line-number --pretty || true)
if [ -n "$MATCH_FILE" ]; then
  FILE_PATH=$(echo "$MATCH_FILE" | sed -n '1p' | cut -d: -f1)
  echo
  echo "----- $FILE_PATH -----"
  sed -n '1,160p' "$FILE_PATH" || true
fi

echo
echo "Show src/wrappers/testUtil.tsx (discovered earlier)"
if [ -f src/wrappers/testUtil.tsx ]; then
  sed -n '1,240p' src/wrappers/testUtil.tsx || true
else
  echo "src/wrappers/testUtil.tsx not found"
fi

echo
echo "Search for any imports from @apollo/client across the repo"
rg -n -C2 "@apollo/client" --hidden --glob '!node_modules/**' || true

echo
echo "Search for other jest.mock('@apollo/client' occurrences"
rg -n "jest\.mock\(['\"]@apollo/client['\"]" --hidden --glob '!node_modules/**' || true

Length of output: 1857


Don't mock @apollo/client down to only gql — pass through actual exports or remove the mock.

Mocking only gql can break code that imports other Apollo exports (ApolloClient, InMemoryCache, useApolloClient, ApolloProvider, HttpLink, etc.). Affected: frontend/tests/unit/pages/CreateModule.test.tsx (≈26–28) and frontend/tests/unit/pages/EditModule.test.tsx (≈26–28).

Apply one of these:

Option A — pass through actual module:

-jest.mock('@apollo/client', () => ({
-  gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => {
+  const actual = jest.requireActual('@apollo/client')
+  return { ...actual }
+})

Option B — remove the mock entirely (preferred if not strictly needed in this test):

-jest.mock('@apollo/client', () => ({
-  gql: jest.requireActual('@apollo/client').gql,
-}))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jest.mock('@apollo/client', () => ({
gql: jest.requireActual('@apollo/client').gql,
}))
jest.mock('@apollo/client', () => {
const actual = jest.requireActual('@apollo/client')
return { ...actual }
})
🤖 Prompt for AI Agents
In frontend/__tests__/unit/pages/CreateModule.test.tsx around lines 26 to 28,
the test currently replaces the entire @apollo/client module with an object
exposing only gql which breaks other imports (ApolloClient, InMemoryCache,
hooks, etc.); fix by removing the jest.mock call entirely or change it to a
pass-through mock so the real module exports are kept (i.e., import the actual
module and return it, optionally overriding only specific pieces you need to
stub), ensuring all other Apollo exports used by the tested components remain
available.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (13)
frontend/__tests__/unit/utils/structuredData.test.ts (2)

1-1: Use import type for type-only imports.

Prevents emitting a runtime import and avoids TS 5 verbatimModuleSyntax issues.

Apply this diff:

-import { User } from 'types/user'
+import type { User } from 'types/user'

29-30: Derive expected ISO timestamps instead of hardcoding .000Z.

Reduces brittleness if formatting changes upstream.

Apply this diff:

   it('should generate valid ProfilePage structured data', () => {
     const result = generateProfilePageStructuredData(mockUser)

+    const expectedDateCreated = new Date(mockUser.createdAt!).toISOString()
+    const expectedDateModified = new Date(mockUser.updatedAt!).toISOString()
+
     expect(result).toEqual({
       '@context': 'https://schema.org',
       '@type': 'ProfilePage',
-      dateCreated: '2020-01-01T00:00:00.000Z',
-      dateModified: '2021-02-03T00:00:00.000Z',
+      dateCreated: expectedDateCreated,
+      dateModified: expectedDateModified,
       mainEntity: {
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)

31-34: Use generated mutation document; remove the TODO.

Replace the legacy mutation constant with the generated document to get end-to-end typing and consistency with the query update.

-  const [updateProgram] = useMutation(UPDATE_PROGRAM_STATUS_MUTATION, {
-    // TODO: update
-    onError: handleAppError,
-  })
+  const [updateProgram] = useMutation(UpdateProgramStatusDocument, {
+    onError: handleAppError,
+  })

Add the import (outside this hunk):

import { UpdateProgramStatusDocument } from 'types/__generated__/programsMutations.generated'

83-83: Fix: calling toLowerCase on enum values can fail type-checking.

Wrap the enum in String() for safe formatting.

-        title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`,
+        title: `Program status updated to ${upperFirst(String(newStatus).toLowerCase())}`,

131-131: Same enum-string formatting nit as above.

-    { label: 'Status', value: upperFirst(program.status.toLowerCase()) },
+    { label: 'Status', value: upperFirst(String(program.status).toLowerCase()) },

13-13: Optional: type state from generated query to avoid drift.

Derive Program/modules state types from GetProgramAndModulesQuery to keep in lockstep with schema changes.

Example (outside this hunk):

import type { GetProgramAndModulesQuery } from 'types/__generated__/graphql'

const [program, setProgram] = useState<GetProgramAndModulesQuery['getProgram'] | null>(null)
const [modules, setModules] = useState<NonNullable<GetProgramAndModulesQuery['getProgramModules']>>([])
frontend/__tests__/unit/pages/EditModule.test.tsx (1)

25-27: Avoid over‑mocking @apollo/client root to only gql (can break other imports).

This stub drops all other exports from @apollo/client at runtime. If the component (or its deps) import anything else (e.g., ApolloError), tests may break. Prefer pass‑through to the actual module.

Apply this diff:

-jest.mock('@apollo/client', () => ({
-  gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => jest.requireActual('@apollo/client'))

Please confirm the component under test doesn’t rely on other @apollo/client exports at runtime.

frontend/src/components/SingleModuleCard.tsx (2)

40-40: Avoid window at render time; use usePathname() to build URLs

Referencing window can break SSR and hydration. Prefer usePathname() from next/navigation.

Apply within the selected lines:

-    router.push(`${window.location.pathname}/modules/${module.key}`)
+    router.push(`${pathname}/modules/${module.key}`)
-    router.push(`${window.location.pathname}/modules/${module.key}/edit`)
+    router.push(`${pathname}/modules/${module.key}/edit`)
-    router.push(`${window.location.pathname}/modules/create`)
+    router.push(`${pathname}/modules/create`)
-            href={`${window.location.pathname}/modules/${module.key}`}
+            href={`${pathname}/modules/${module.key}`}

And outside these ranges, also update:

// imports
import { useRouter, usePathname } from 'next/navigation'

// after const router = useRouter()
const pathname = usePathname()

Note: Aware of your preference to keep migration PRs focused. Consider as a small follow-up if out of scope.

Also applies to: 45-45, 50-50, 76-76


1-1: Confirm client component usage or add 'use client'

This file uses React/Next client hooks. Ensure it's only imported by client components, or add the directive.

'use client'
frontend/__tests__/unit/pages/About.test.tsx (4)

15-18: Type the mocked useQuery to avoid unknown as jest.Mock

Use a typed alias so you don’t repeat casts and you get signatures checked.

Add once near the imports:

// At the top (after imports)
const mockedUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>

Then replace usages like:

-;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
+;mockedUseQuery.mockImplementation((query, options) => {

(Apply similarly where you reassign the implementation in individual tests.)


119-136: DRY up repeated useQuery mocks

You’re re-declaring similar mockImplementations many times. Extracting small helpers will reduce duplication and ease future changes.

Example helper (place near the top of the file):

type Key = string | undefined;
const byKey = (key: Key, expected: Key) => key === expected;

function defaultAboutUseQueryMock(query: any, options?: { variables?: { key?: string } }) {
  const key = options?.variables?.key;
  if (query === GetProjectMetadataDocument && byKey(key, 'nest')) return mockProjectData;
  if (query === GetTopContributorsDocument && byKey(key, 'nest')) return mockTopContributorsData;
  if (query === GetLeaderDataDocument) return mockUserData(key);
  return { loading: true };
}

Then in beforeEach:

-;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { ... })
+mockedUseQuery.mockImplementation(defaultAboutUseQueryMock)

And in specialized tests, override only the differing branch, delegating the rest to the default to avoid breaking unrelated queries.

Also applies to: 178-187, 311-323, 342-354, 373-381, 395-405, 445-454, 468-480, 491-501, 513-523, 535-545, 561-571


178-187: Fix incorrect error shape in mock

You return { error: mockError }, where mockError itself is { error: Error }. Apollo expects error to be an Error/ApolloError, not a nested object.

Apply:

-      } else if (options?.variables?.key === 'arkid15r') {
-        return { data: null, loading: false, error: mockError }
+      } else if (options?.variables?.key === 'arkid15r') {
+        return { data: null, loading: false, error: new Error('GraphQL error') }

Alternatively, redefine mockError to be an Error:

// near line 112
-const mockError = {
-  error: new Error('GraphQL error'),
-}
+const mockError = new Error('GraphQL error')

140-142: Replace jest.clearAllMocks() with jest.resetAllMocks()

clearAllMocks() doesn't reset mock implementations and can leak state across tests; resetAllMocks() clears calls and restores default implementations.

-    jest.clearAllMocks()
+    jest.resetAllMocks()

rg found 80 occurrences under frontend/tests; update this file and consider a project-wide replace or set "resetMocks" in Jest config — verify any tests that rely on preserved mock implementations before changing.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32da88b and 04081e9.

📒 Files selected for processing (9)
  • frontend/__tests__/unit/data/mockProgramData.ts (2 hunks)
  • frontend/__tests__/unit/pages/About.test.tsx (13 hunks)
  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/CreateModule.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/EditModule.test.tsx (5 hunks)
  • frontend/__tests__/unit/utils/structuredData.test.ts (3 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4 hunks)
  • frontend/src/components/SingleModuleCard.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/tests/unit/pages/CreateModule.test.tsx
  • frontend/tests/unit/pages/ApiKeysPage.test.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/__tests__/unit/pages/EditModule.test.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/__tests__/unit/data/mockProgramData.ts (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
  • ProgramStatusEnum (22-27)
frontend/__tests__/unit/pages/About.test.tsx (2)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
  • GetProjectMetadataDocument (37-37)
  • GetTopContributorsDocument (38-38)
frontend/src/types/__generated__/userQueries.generated.ts (1)
  • GetLeaderDataDocument (26-26)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/app/global-error.tsx (1)
  • handleAppError (66-86)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetProgramAndModulesDocument (37-37)
🔇 Additional comments (17)
frontend/__tests__/unit/utils/structuredData.test.ts (2)

5-5: Switch to User type looks correct.

Matches the exported type in types/user.ts.


61-61: LGTM on minimal user typing.

Covers required fields; optional fields omitted as intended.

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)

2-2: Apollo v4 hooks import looks good.

Switching hooks to '@apollo/client/react' aligns with the v4 migration.


10-11: Good move to generated enums/documents.

Using ProgramStatusEnum and GetProgramAndModulesDocument is consistent with codegen-driven v4 usage.


40-40: Query migration to generated document is correct.

Typed variables + skip usage are appropriate.


79-79: RefetchQuery shape is valid in v4; consider awaiting if UI depends on it.

Only add awaitRefetchQueries: true if you need the refetch to complete before proceeding (e.g., before reading cache/UI that depends on fresh data).


132-133: Duplicate of earlier feedback: guard/normalize date scalar before formatting.

Previously flagged: formatDate may throw if startedAt/endedAt aren’t reliably strings. Consider a presence check or scalar mapping in codegen to ensure these are strings.

frontend/__tests__/unit/pages/EditModule.test.tsx (5)

1-1: Good: Hooks imported from @apollo/client/react align with Apollo v4 and your mocks.


19-23: Good: Mocking @apollo/client/react directly avoids the prior import/mock mismatch.


61-80: Mock shape for useQuery looks correct for this test flow.


81-84: Mutation mock wiring is correct and verifies the submit path.


125-125: Loading state mock aligns with the spinner assertion.

frontend/__tests__/unit/data/mockProgramData.ts (1)

1-1: Confirm generated enum export and Jest path alias

  • Confirm ProgramStatusEnum is emitted as an exported enum (not generated as a type via enumsAsTypes).
  • Confirm the 'types/*' tsconfig path alias resolves under Jest (moduleNameMapper/ts-jest); check frontend/tests/unit/data/mockProgramData.ts — import { ProgramStatusEnum } from 'types/generated/graphql'
frontend/__tests__/unit/pages/About.test.tsx (4)

1-1: Apollo v4 hooks import path is correct

Switching to '@apollo/client/react' aligns with Apollo Client v4. Looks good.


535-545: LGTM: Project error toast path is validated correctly

Matching the query by GetProjectMetadataDocument and asserting addToast payload looks solid.


561-571: LGTM: Top contributors error toast path is validated

Same pattern as the project error case; assertions look correct.


9-14: Good move — About page uses generated DocumentNodes

frontend/src/app/about/page.tsx imports and calls GetProjectMetadataDocument, GetTopContributorsDocument, and GetLeaderDataDocument with useQuery, so tests' identity checks remain stable.


const moduleDetails = [
{ label: 'Experience Level', value: upperFirst(module.experienceLevel) },
{ label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against null/undefined before calling toLowerCase()

This will throw if module.experienceLevel is nullish. Keep the existing "Unknown" fallback by returning undefined when absent.

-    { label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) },
+    { label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{ label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) },
{ label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined },
🤖 Prompt for AI Agents
In frontend/src/components/SingleModuleCard.tsx around line 54, the expression
calls module.experienceLevel.toLowerCase() without guarding for null/undefined
which will throw; change it to first check that module.experienceLevel is
non-nullish and only then call toLowerCase and upperFirst, otherwise return
undefined to preserve the existing "Unknown" fallback (e.g., use a conditional
or optional chaining with a nullish check so the value becomes
upperFirst(module.experienceLevel.toLowerCase()) when present, or undefined when
absent).

@rudransh-shrivastava rudransh-shrivastava marked this pull request as ready for review September 23, 2025 13:04
@rudransh-shrivastava rudransh-shrivastava marked this pull request as draft September 23, 2025 13:04
@rudransh-shrivastava
Copy link
Collaborator Author

rudransh-shrivastava commented Sep 23, 2025

sorry, accidentally clicked "Ready for review".
However, all tests should pass now.

I still have some more refactoring to do.

  • change wrong Enum value usages.
  • some pages use the old queries and not typed document node.
  • address bot comments

I also have to verify all the changes. This is not ready for review yet.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (12)
frontend/__tests__/unit/utils/structuredData.test.ts (1)

1-1: Prefer type-only import for User in frontend/tests/unit/utils/structuredData.test.ts

Use import type so the symbol is elided at compile time and avoids unintended runtime imports.

-import { User } from 'types/user'
+import type { User } from 'types/user'
frontend/__tests__/unit/pages/About.test.tsx (5)

178-188: Return an Error instance, not an object containing one.

The current branch returns { error: { error: Error } }, which can break code expecting error instanceof Error.

Apply this diff:

-      } else if (options?.variables?.key === 'arkid15r') {
-        return { data: null, loading: false, error: mockError }
+      } else if (options?.variables?.key === 'arkid15r') {
+        return { data: null, loading: false, error: new Error('GraphQL error') }

468-480: Use null (or undefined) for error, not false.

A boolean false for error can cause type mismatches and brittle checks. Prefer null.

Apply this diff:

-      } else if (options?.variables?.key === 'arkid15r') {
-        return { data: null, loading: false, error: false }
+      } else if (options?.variables?.key === 'arkid15r') {
+        return { data: null, loading: false, error: null }

513-523: Optional: qualify by document for stricter shaping.

You’re matching only on key === 'nest', which also affects non-project queries. Guarding by the specific document avoids returning the wrong data shape to other queries.

Apply this diff:

-    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
-      if (options?.variables?.key === 'nest') {
-        return { loading: false, data: { project: null }, error: null }
-      }
+    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
+      if (query === GetProjectMetadataDocument && options?.variables?.key === 'nest') {
+        return { loading: false, data: { project: null }, error: null }
+      }
       return {
         loading: false,
         data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } },
         error: null,
       }
     })

535-544: Avoid returning mismatched shapes for other queries in this override.

Currently the fallback returns { data: { user: ... } } even for non‑user queries, which can break consumers expecting project/topContributors.

Apply this diff:

-    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
-      if (query === GetProjectMetadataDocument && options?.variables?.key === 'nest') {
-        return { loading: false, data: null, error: new Error('GraphQL error') }
-      }
-      return {
-        loading: false,
-        data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } },
-        error: null,
-      }
-    })
+    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
+      const key = options?.variables?.key
+      if (query === GetProjectMetadataDocument && key === 'nest') {
+        return { loading: false, data: null, error: new Error('GraphQL error') }
+      }
+      if (query === GetTopContributorsDocument && key === 'nest') {
+        return { loading: false, data: { topContributors: [] }, error: null }
+      }
+      if (query === GetLeaderDataDocument) {
+        return {
+          loading: false,
+          data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } },
+          error: null,
+        }
+      }
+      return { loading: true }
+    })

561-570: Same here: return shapes matching the queried document.

Ensure project, topContributors, and user queries each receive compatible data.

Apply this diff:

-    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
-      if (query === GetTopContributorsDocument && options?.variables?.key === 'nest') {
-        return { loading: false, data: null, error: new Error('GraphQL error') }
-      }
-      return {
-        loading: false,
-        data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } },
-        error: null,
-      }
-    })
+    ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => {
+      const key = options?.variables?.key
+      if (query === GetTopContributorsDocument && key === 'nest') {
+        return { loading: false, data: null, error: new Error('GraphQL error') }
+      }
+      if (query === GetProjectMetadataDocument && key === 'nest') {
+        return mockProjectData
+      }
+      if (query === GetLeaderDataDocument) {
+        return {
+          loading: false,
+          data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } },
+          error: null,
+        }
+      }
+      return { loading: true }
+    })
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)

31-34: Resolve TODO: migrate mutation to generated document/types.

Follow the same pattern as queries (use UpdateProgramStatusDocument and typed variables) when ready.

I can draft the minimal mutation migration if you want it in this PR.


79-79: Optionally await refetchQueries to ensure UI consistency before toast.

If you want the toast after fresh data is in cache, add awaitRefetchQueries: true.

Apply within the mutate call:

-        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
+        refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }],
+        awaitRefetchQueries: true,

83-83: Type-safe enum formatting: cast before toLowerCase.

Depending on codegen (enum vs union), ProgramStatusEnum may not have string methods at the type level.

Use a safe cast:

-        title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`,
+        title: `Program status updated to ${upperFirst(String(newStatus).toLowerCase())}`,
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (3)

54-55: Tighten mock typings (optional).
Use jest.MockedFunction to avoid unknown casts.

-const mockUseQuery = useQuery as unknown as jest.Mock
-const mockUseMutation = useMutation as unknown as jest.Mock
+const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>
+const mockUseMutation = useMutation as jest.MockedFunction<typeof useMutation>

71-79: Document identity checks can be brittle; prefer matching by operation name (optional).
In some build/test setups the same doc can be re-instantiated, breaking strict equality.

-    mockUseMutation.mockImplementation((mutation) => {
-      if (mutation === CreateApiKeyDocument) {
+    mockUseMutation.mockImplementation((mutation: any) => {
+      const opName = mutation?.definitions?.[0]?.name?.value
+      if (opName === 'CreateApiKey') {
         return [mockCreateMutation, { loading: false }]
       }
-      if (mutation === RevokeApiKeyDocument) {
+      if (opName === 'RevokeApiKey') {
         return [mockRevokeMutation, { loading: false }]
       }
       return [jest.fn(), { loading: false }]
     })

198-205: Reduce potential timezone flakiness in ISO expectation (optional).
Hard-coding the full ISO string can be fragile across environments. Loosen to prefix match.

-          expiresAt: new Date('2025-12-31T00:00:00.000Z').toISOString(),
+          expiresAt: expect.stringMatching(/^2025-12-31/),
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32da88b and 04081e9.

📒 Files selected for processing (9)
  • frontend/__tests__/unit/data/mockProgramData.ts (2 hunks)
  • frontend/__tests__/unit/pages/About.test.tsx (13 hunks)
  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (5 hunks)
  • frontend/__tests__/unit/pages/CreateModule.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/EditModule.test.tsx (5 hunks)
  • frontend/__tests__/unit/utils/structuredData.test.ts (3 hunks)
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4 hunks)
  • frontend/src/components/SingleModuleCard.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/tests/unit/data/mockProgramData.ts
  • frontend/tests/unit/pages/CreateModule.test.tsx
  • frontend/src/app/mentorship/programs/[programKey]/page.tsx
  • frontend/tests/unit/pages/EditModule.test.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.

Applied to files:

  • frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
  • User (10-32)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/app/global-error.tsx (1)
  • handleAppError (66-86)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
  • GetProgramAndModulesDocument (37-37)
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (2)
  • CreateApiKeyDocument (26-26)
  • RevokeApiKeyDocument (27-27)
frontend/__tests__/unit/pages/About.test.tsx (3)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
  • GetProjectMetadataDocument (37-37)
  • GetTopContributorsDocument (38-38)
frontend/__tests__/unit/data/mockProjectData.ts (1)
  • mockProjectData (1-26)
frontend/src/types/__generated__/userQueries.generated.ts (1)
  • GetLeaderDataDocument (26-26)
🔇 Additional comments (23)
frontend/src/components/SingleModuleCard.tsx (1)

54-54: Guard against null/undefined before calling toLowerCase()

This throws if module.experienceLevel is nullish. Preserve the existing "Unknown" fallback by returning undefined when absent.

-    { label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) },
+    { label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined },
frontend/__tests__/unit/utils/structuredData.test.ts (3)

5-5: LGTM: typing mockUser with User aligns with the updated util signature


29-30: LGTM: ISO8601 with millisecond precision matches the new formatting behavior


61-61: LGTM: minimalUser typed as User keeps the fallback-to-login case clear

frontend/__tests__/unit/pages/About.test.tsx (10)

1-1: Correct Apollo v4 React import path.

Importing hooks from '@apollo/client/react' is the right target for v4 React APIs.


9-14: Good switch to generated documents.

Using GetProjectMetadataDocument, GetTopContributorsDocument, and GetLeaderDataDocument aligns tests with graphql-codegen outputs.


15-18: Appropriate mocking of React hooks entrypoint.

Mocking '@apollo/client/react' and overriding useQuery is consistent with the migration.


119-136: Baseline mock aligned to v4 doc identity checks.

Document equality checks and variable-based routing look good.


311-323: Loading-branch mock reads clearly.

Accurately simulates a single leader in loading state.


342-354: Null user data branch is fine.

Correctly exercises the null-handling path.


373-381: Project null branch is fine.

This matches the error view flow for missing project data.


395-405: Undefined user data case covered.

Good coverage for an undefined payload edge case.


445-455: Partial user data case covered.

Good fallback expectation (login when name is missing).


491-501: Loading spinner setup looks good.

Mocks return loading for project; others return stable user data.

frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)

10-11: Good switch to generated GraphQL artifacts.

Enum + document-based API adoption aligns with Apollo v4 and codegen.


13-13: Confirm local types match generated query shape.

Using Program/Module from 'types/mentorship' is OK, but please verify they align with GetProgramAndModules results to prevent subtle drift.


40-40: LGTM: document-based useQuery.

Correct migration to the generated DocumentNode.


131-131: LGTM on status normalization.

Formatting via lowerCase + upperFirst is fine.

Note: As previously flagged, adjacent date fields (Lines 132-133) may be null/unknown from the API; guarding before formatDate would harden runtime. Acknowledging this may be out-of-scope for the migration PR.


2-2: Standardize Apollo import path; verify v4 subpath usage.
Using '@apollo/client/react' is fine in Apollo Client v4 — ensure all React-hook imports use the same subpath to avoid duplicate bundles and mocking friction.
Observed in frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
Run locally to audit mixed imports:

# from repo root
git grep -n "@apollo/client" || true
# or (if using ripgrep)
rg -n -S "@apollo/client" -g '!node_modules/**' -g '!dist/**' || true
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (4)

9-12: Switch to generated DocumentNodes is on point.
Imports align with codegen outputs and Apollo v4 doc-based workflow.


14-18: Mock path updated correctly; confirm Jest hoisting in your setup.
This pattern relies on Jest hoisting jest.mock. If running ESM without hoisting, move this mock above imports or convert to an explicit factory mock.


175-176: Correct to assert DateTime variable as string.
Matches typical GraphQL DateTime transport semantics.


2-2: Apollo v4 hooks import path is correct — no leftover hook imports from '@apollo/client' found.

Repo-wide search: hooks (useQuery/useMutation/useApolloClient/etc.) are imported from '@apollo/client/react'; remaining '@apollo/client' usages are gql/ApolloClient (expected). Tests frontend/tests/unit/pages/EditModule.test.tsx and CreateModule.test.tsx mock '@apollo/client' to expose gql via requireActual — acceptable.

Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
frontend/__tests__/unit/components/ProgramActions.test.tsx (1)

69-87: Assertions now use enum values — good; consider passing enum to props too

Using ProgramStatusEnum in expectations is correct. To avoid string/enum drift, pass enum values to the component’s status prop in these tests as well.

Apply this diff in this file to pass enum values (repeat the pattern for remaining cases):

-    render(<ProgramActions status="DRAFT" setStatus={setStatus} />)
+    render(<ProgramActions status={ProgramStatusEnum.Draft} setStatus={setStatus} />)

-    render(<ProgramActions status="PUBLISHED" setStatus={setStatus} />)
+    render(<ProgramActions status={ProgramStatusEnum.Published} setStatus={setStatus} />)
frontend/src/components/ProgramActions.tsx (1)

10-15: Type the status prop as ProgramStatusEnum and simplify setStatus type

You compare against ProgramStatusEnum.* but type status as string. Make it ProgramStatusEnum for stronger safety. The union for setStatus is unnecessary if those are the only members.

Apply:

 interface ProgramActionsProps {
-  status: string
-  setStatus: (
-    newStatus: ProgramStatusEnum.Draft | ProgramStatusEnum.Published | ProgramStatusEnum.Completed
-  ) => void
+  status: ProgramStatusEnum
+  setStatus: (newStatus: ProgramStatusEnum) => void
 }
frontend/__tests__/unit/pages/EditProgram.test.tsx (1)

39-39: Reduce repeated casts for useQuery mocks

Define a typed alias once to avoid (unknown as jest.Mock) repetition.

Example:

-import { useQuery } from '@apollo/client/react'
+import { useQuery } from '@apollo/client/react'
+const mockedUseQuery = useQuery as unknown as jest.Mock
...
-;(useQuery as unknown as jest.Mock).mockReturnValue({ loading: true })
+mockedUseQuery.mockReturnValue({ loading: true })

Also applies to: 51-59, 72-87

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75ab358 and 28b7fa8.

📒 Files selected for processing (10)
  • frontend/__tests__/unit/components/ProgramActions.test.tsx (3 hunks)
  • frontend/__tests__/unit/pages/EditModule.test.tsx (6 hunks)
  • frontend/__tests__/unit/pages/EditProgram.test.tsx (6 hunks)
  • frontend/src/app/my/mentorship/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (4 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (3 hunks)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx (3 hunks)
  • frontend/src/components/ModuleForm.tsx (3 hunks)
  • frontend/src/components/ProgramActions.tsx (3 hunks)
  • frontend/src/types/mentorship.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
  • frontend/src/components/ModuleForm.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/page.tsx
  • frontend/src/types/mentorship.ts
  • frontend/tests/unit/pages/EditModule.test.tsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.

Applied to files:

  • frontend/__tests__/unit/pages/EditProgram.test.tsx
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.

Applied to files:

  • frontend/__tests__/unit/pages/EditProgram.test.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.

Applied to files:

  • frontend/__tests__/unit/pages/EditProgram.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run frontend e2e tests
🔇 Additional comments (5)
frontend/__tests__/unit/components/ProgramActions.test.tsx (1)

5-5: LGTM: generated enum import is correct

Enum import aligns with codegen changes.

frontend/src/components/ProgramActions.tsx (2)

30-38: LGTM: setting enum values on actions

Switching to ProgramStatusEnum.* for state transitions is correct and consistent with codegen.


46-52: LGTM: option visibility checks using enums

Conditions read clearly and align with the updated enum semantics.

Confirm that all call sites now pass ProgramStatusEnum (or the matching string values) until the prop type is tightened per the above diff.

frontend/__tests__/unit/pages/EditProgram.test.tsx (2)

1-1: LGTM: import Apollo React hooks from @apollo/client/react

Matches Apollo v4 guidance.


15-22: LGTM: mocking the React entrypoint

Mocking @apollo/client/react (and spreading the actual) is correct for v4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate to Apollo 4.0 from 3.14
1 participant